From 82b86decdfb6e789a8e23c7ddfb5a25e8a5694d7 Mon Sep 17 00:00:00 2001 From: Thom de Jong Date: Wed, 12 Nov 2025 21:11:06 +0100 Subject: [PATCH 1/5] Change G-code editor buttons --- src/Mod/CAM/Path/Post/Utils.py | 16 ++++++++-- src/Mod/CAM/Path/Post/UtilsExport.py | 45 +++++++++++++++------------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/Mod/CAM/Path/Post/Utils.py b/src/Mod/CAM/Path/Post/Utils.py index f71a26f848..0b43cb0b25 100644 --- a/src/Mod/CAM/Path/Post/Utils.py +++ b/src/Mod/CAM/Path/Post/Utils.py @@ -227,7 +227,9 @@ class GCodeEditorDialog(QtGui.QDialog): # OK and Cancel buttons self.buttons = QtGui.QDialogButtonBox( - QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel, + QtGui.QDialogButtonBox.Apply + | QtGui.QDialogButtonBox.Discard + | QtGui.QDialogButtonBox.Cancel, QtCore.Qt.Horizontal, self, ) @@ -245,8 +247,16 @@ class GCodeEditorDialog(QtGui.QDialog): if width > 0 and height > 0: self.resize(width, height) - self.buttons.accepted.connect(self.accept) - self.buttons.rejected.connect(self.reject) + self.buttons.clicked.connect(self.clicked) + + def clicked(self, button): + match self.buttons.buttonRole(button): + case QtGui.QDialogButtonBox.RejectRole: + self.done(0) + case QtGui.QDialogButtonBox.ApplyRole: + self.done(1) + case QtGui.QDialogButtonBox.DestructiveRole: + self.done(2) def done(self, *args, **kwargs): params = FreeCAD.ParamGet(self.paramKey) diff --git a/src/Mod/CAM/Path/Post/UtilsExport.py b/src/Mod/CAM/Path/Post/UtilsExport.py index 1a503978c4..4942486a3f 100644 --- a/src/Mod/CAM/Path/Post/UtilsExport.py +++ b/src/Mod/CAM/Path/Post/UtilsExport.py @@ -275,6 +275,7 @@ def export_common(values: Values, objectslist, filename: str) -> str: final: str final_for_editor: str gcode: Gcode = [] + editor_result: int for obj in objectslist: if not hasattr(obj, "Path"): @@ -339,12 +340,14 @@ def export_common(values: Values, objectslist, filename: str) -> str: # the editor expects lines to end in "\n", and returns lines ending in "\n" if values["END_OF_LINE_CHARACTERS"] == "\n": dia.editor.setText(final) - if dia.exec_(): + editor_result = dia.exec_() + if editor_result == 1: final = dia.editor.toPlainText() else: final_for_editor = "\n".join(gcode) dia.editor.setText(final_for_editor) - if dia.exec_(): + editor_result = dia.exec_() + if editor_result == 1: final_for_editor = dia.editor.toPlainText() # convert all "\n" to the appropriate end-of-line characters if values["END_OF_LINE_CHARACTERS"] == "\n\n": @@ -360,21 +363,23 @@ def export_common(values: Values, objectslist, filename: str) -> str: print("done postprocessing.") - if not filename == "-": - if final[0:2] == "\n\n": - # write out the gcode using "\n" as the end-of-line characters - with open(filename, "w", encoding="utf-8", newline="") as gfile: - gfile.write(final[2:]) - elif "\r" in final: - with open(filename, "w", encoding="utf-8", newline="") as gfile: - # write out the gcode with whatever end-of-line characters it already has, - # presumably either "\r" or "\r\n" - gfile.write(final) - else: - with open(filename, "w", encoding="utf-8", newline=None) as gfile: - # The gcode has "\n" as the end-of-line characters, which means - # "write out the gcode with whatever end-of-line characters the system - # that is running the postprocessor uses". - gfile.write(final) - - return final + if editor_result in [0, 1]: + if not filename == "-": + if final[0:2] == "\n\n": + # write out the gcode using "\n" as the end-of-line characters + with open(filename, "w", encoding="utf-8", newline="") as gfile: + gfile.write(final[2:]) + elif "\r" in final: + with open(filename, "w", encoding="utf-8", newline="") as gfile: + # write out the gcode with whatever end-of-line characters it already has, + # presumably either "\r" or "\r\n" + gfile.write(final) + else: + with open(filename, "w", encoding="utf-8", newline=None) as gfile: + # The gcode has "\n" as the end-of-line characters, which means + # "write out the gcode with whatever end-of-line characters the system + # that is running the postprocessor uses". + gfile.write(final) + return final + else: + return None From 5cc2c9c7e15a7020018b7e221ef27fc3381b7345 Mon Sep 17 00:00:00 2001 From: Thom de Jong Date: Thu, 13 Nov 2025 13:22:52 +0100 Subject: [PATCH 2/5] Fix editor_result check --- src/Mod/CAM/Path/Post/UtilsExport.py | 38 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Mod/CAM/Path/Post/UtilsExport.py b/src/Mod/CAM/Path/Post/UtilsExport.py index 4942486a3f..c00cbd3592 100644 --- a/src/Mod/CAM/Path/Post/UtilsExport.py +++ b/src/Mod/CAM/Path/Post/UtilsExport.py @@ -363,23 +363,23 @@ def export_common(values: Values, objectslist, filename: str) -> str: print("done postprocessing.") - if editor_result in [0, 1]: - if not filename == "-": - if final[0:2] == "\n\n": - # write out the gcode using "\n" as the end-of-line characters - with open(filename, "w", encoding="utf-8", newline="") as gfile: - gfile.write(final[2:]) - elif "\r" in final: - with open(filename, "w", encoding="utf-8", newline="") as gfile: - # write out the gcode with whatever end-of-line characters it already has, - # presumably either "\r" or "\r\n" - gfile.write(final) - else: - with open(filename, "w", encoding="utf-8", newline=None) as gfile: - # The gcode has "\n" as the end-of-line characters, which means - # "write out the gcode with whatever end-of-line characters the system - # that is running the postprocessor uses". - gfile.write(final) - return final - else: + if editor_result == 0: return None + + if not filename == "-": + if final[0:2] == "\n\n": + # write out the gcode using "\n" as the end-of-line characters + with open(filename, "w", encoding="utf-8", newline="") as gfile: + gfile.write(final[2:]) + elif "\r" in final: + with open(filename, "w", encoding="utf-8", newline="") as gfile: + # write out the gcode with whatever end-of-line characters it already has, + # presumably either "\r" or "\r\n" + gfile.write(final) + else: + with open(filename, "w", encoding="utf-8", newline=None) as gfile: + # The gcode has "\n" as the end-of-line characters, which means + # "write out the gcode with whatever end-of-line characters the system + # that is running the postprocessor uses". + gfile.write(final) + return final From 39ef7c8e3571e646350c0e6371290fb82f8fa94b Mon Sep 17 00:00:00 2001 From: Thom de Jong Date: Thu, 13 Nov 2025 15:25:22 +0100 Subject: [PATCH 3/5] Initialise editor_result --- src/Mod/CAM/Path/Post/UtilsExport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/CAM/Path/Post/UtilsExport.py b/src/Mod/CAM/Path/Post/UtilsExport.py index c00cbd3592..03e082269c 100644 --- a/src/Mod/CAM/Path/Post/UtilsExport.py +++ b/src/Mod/CAM/Path/Post/UtilsExport.py @@ -275,7 +275,7 @@ def export_common(values: Values, objectslist, filename: str) -> str: final: str final_for_editor: str gcode: Gcode = [] - editor_result: int + editor_result: int = 1 for obj in objectslist: if not hasattr(obj, "Path"): From c36b2183613950804eba56ae04176b452152a3ba Mon Sep 17 00:00:00 2001 From: Thom de Jong Date: Thu, 13 Nov 2025 17:38:31 +0100 Subject: [PATCH 4/5] Keep old buttons for old post processors --- src/Mod/CAM/Path/Post/Utils.py | 27 +++++++++++++++++---------- src/Mod/CAM/Path/Post/UtilsExport.py | 7 ++++--- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Mod/CAM/Path/Post/Utils.py b/src/Mod/CAM/Path/Post/Utils.py index 0b43cb0b25..9c01bf2030 100644 --- a/src/Mod/CAM/Path/Post/Utils.py +++ b/src/Mod/CAM/Path/Post/Utils.py @@ -208,7 +208,7 @@ class GCodeHighlighter(QtGui.QSyntaxHighlighter): class GCodeEditorDialog(QtGui.QDialog): - def __init__(self, parent=None): + def __init__(self, parent=None, refactored=False): if parent is None: parent = FreeCADGui.getMainWindow() QtGui.QDialog.__init__(self, parent) @@ -225,14 +225,21 @@ class GCodeEditorDialog(QtGui.QDialog): self.editor.setText("G01 X55 Y4.5 F300.0") layout.addWidget(self.editor) - # OK and Cancel buttons - self.buttons = QtGui.QDialogButtonBox( - QtGui.QDialogButtonBox.Apply - | QtGui.QDialogButtonBox.Discard - | QtGui.QDialogButtonBox.Cancel, - QtCore.Qt.Horizontal, - self, - ) + # buttons depending on the post processor used + if refactored: + self.buttons = QtGui.QDialogButtonBox( + QtGui.QDialogButtonBox.Apply + | QtGui.QDialogButtonBox.Discard + | QtGui.QDialogButtonBox.Cancel, + QtCore.Qt.Horizontal, + self, + ) + else: + self.buttons = QtGui.QDialogButtonBox( + QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel, + QtCore.Qt.Horizontal, + self, + ) layout.addWidget(self.buttons) # restore placement and size @@ -253,7 +260,7 @@ class GCodeEditorDialog(QtGui.QDialog): match self.buttons.buttonRole(button): case QtGui.QDialogButtonBox.RejectRole: self.done(0) - case QtGui.QDialogButtonBox.ApplyRole: + case QtGui.QDialogButtonBox.ApplyRole | QtGui.QDialogButtonBox.AcceptRole: self.done(1) case QtGui.QDialogButtonBox.DestructiveRole: self.done(2) diff --git a/src/Mod/CAM/Path/Post/UtilsExport.py b/src/Mod/CAM/Path/Post/UtilsExport.py index 03e082269c..da5023548f 100644 --- a/src/Mod/CAM/Path/Post/UtilsExport.py +++ b/src/Mod/CAM/Path/Post/UtilsExport.py @@ -336,7 +336,7 @@ def export_common(values: Values, objectslist, filename: str) -> str: if len(final) > 100000: print("Skipping editor since output is greater than 100kb") else: - dia = PostUtils.GCodeEditorDialog() + dia = PostUtils.GCodeEditorDialog(refactored=True) # the editor expects lines to end in "\n", and returns lines ending in "\n" if values["END_OF_LINE_CHARACTERS"] == "\n": dia.editor.setText(final) @@ -361,9 +361,8 @@ def export_common(values: Values, objectslist, filename: str) -> str: # "\r\n" means "use \r\n" final = final_for_editor.replace("\n", values["END_OF_LINE_CHARACTERS"]) - print("done postprocessing.") - if editor_result == 0: + print("canceled postprocessing.") return None if not filename == "-": @@ -382,4 +381,6 @@ def export_common(values: Values, objectslist, filename: str) -> str: # "write out the gcode with whatever end-of-line characters the system # that is running the postprocessor uses". gfile.write(final) + + print("done postprocessing.") return final From 5822d7c7bb155660d3fce05f928b2650b88ea6b1 Mon Sep 17 00:00:00 2001 From: Thom de Jong Date: Tue, 25 Nov 2025 22:10:32 +0100 Subject: [PATCH 5/5] Change button text and disable OK when text unchanged --- src/Mod/CAM/Path/Post/Utils.py | 23 ++++++++++++++++++++--- src/Mod/CAM/Path/Post/UtilsExport.py | 7 +++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Mod/CAM/Path/Post/Utils.py b/src/Mod/CAM/Path/Post/Utils.py index 9c01bf2030..7a694e1990 100644 --- a/src/Mod/CAM/Path/Post/Utils.py +++ b/src/Mod/CAM/Path/Post/Utils.py @@ -208,7 +208,7 @@ class GCodeHighlighter(QtGui.QSyntaxHighlighter): class GCodeEditorDialog(QtGui.QDialog): - def __init__(self, parent=None, refactored=False): + def __init__(self, text="", parent=None, refactored=False): if parent is None: parent = FreeCADGui.getMainWindow() QtGui.QDialog.__init__(self, parent) @@ -222,24 +222,35 @@ class GCodeEditorDialog(QtGui.QDialog): font.setFixedPitch(True) font.setPointSize(10) self.editor.setFont(font) - self.editor.setText("G01 X55 Y4.5 F300.0") + self.editor.setText(text) layout.addWidget(self.editor) # buttons depending on the post processor used if refactored: self.buttons = QtGui.QDialogButtonBox( - QtGui.QDialogButtonBox.Apply + QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Discard | QtGui.QDialogButtonBox.Cancel, QtCore.Qt.Horizontal, self, ) + # Swap the button text as to not change the old cancel behaviour for the user + self.buttons.button(QtGui.QDialogButtonBox.Discard).setIcon( + self.buttons.button(QtGui.QDialogButtonBox.Cancel).icon() + ) + self.buttons.button(QtGui.QDialogButtonBox.Discard).setText( + self.buttons.button(QtGui.QDialogButtonBox.Cancel).text() + ) + self.buttons.button(QtGui.QDialogButtonBox.Cancel).setIcon(QtGui.QIcon()) + self.buttons.button(QtGui.QDialogButtonBox.Cancel).setText("Abort") else: self.buttons = QtGui.QDialogButtonBox( QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel, QtCore.Qt.Horizontal, self, ) + + self.buttons.button(QtGui.QDialogButtonBox.Ok).setDisabled(True) layout.addWidget(self.buttons) # restore placement and size @@ -254,8 +265,13 @@ class GCodeEditorDialog(QtGui.QDialog): if width > 0 and height > 0: self.resize(width, height) + # connect signals + self.editor.textChanged.connect(self.text_changed) self.buttons.clicked.connect(self.clicked) + def text_changed(self): + self.buttons.button(QtGui.QDialogButtonBox.Ok).setDisabled(False) + def clicked(self, button): match self.buttons.buttonRole(button): case QtGui.QDialogButtonBox.RejectRole: @@ -320,6 +336,7 @@ def editor(gcode): dia = GCodeEditorDialog() dia.editor.setText(gcode) + dia.buttons.button(QtGui.QDialogButtonBox.Ok).setDisabled(True) gcodeSize = len(dia.editor.toPlainText()) if gcodeSize <= mhs: # because of poor performance, syntax highlighting is diff --git a/src/Mod/CAM/Path/Post/UtilsExport.py b/src/Mod/CAM/Path/Post/UtilsExport.py index da5023548f..3e77638e3a 100644 --- a/src/Mod/CAM/Path/Post/UtilsExport.py +++ b/src/Mod/CAM/Path/Post/UtilsExport.py @@ -336,16 +336,15 @@ def export_common(values: Values, objectslist, filename: str) -> str: if len(final) > 100000: print("Skipping editor since output is greater than 100kb") else: - dia = PostUtils.GCodeEditorDialog(refactored=True) # the editor expects lines to end in "\n", and returns lines ending in "\n" if values["END_OF_LINE_CHARACTERS"] == "\n": - dia.editor.setText(final) + dia = PostUtils.GCodeEditorDialog(final, refactored=True) editor_result = dia.exec_() if editor_result == 1: final = dia.editor.toPlainText() else: final_for_editor = "\n".join(gcode) - dia.editor.setText(final_for_editor) + dia = PostUtils.GCodeEditorDialog(final_for_editor, refactored=True) editor_result = dia.exec_() if editor_result == 1: final_for_editor = dia.editor.toPlainText() @@ -362,7 +361,7 @@ def export_common(values: Values, objectslist, filename: str) -> str: final = final_for_editor.replace("\n", values["END_OF_LINE_CHARACTERS"]) if editor_result == 0: - print("canceled postprocessing.") + print("aborted postprocessing.") return None if not filename == "-":