diff --git a/src/Mod/CAM/CMakeLists.txt b/src/Mod/CAM/CMakeLists.txt index a044b516d5..ef3b78a2b1 100644 --- a/src/Mod/CAM/CMakeLists.txt +++ b/src/Mod/CAM/CMakeLists.txt @@ -93,6 +93,7 @@ SET(PathPythonMain_SRCS SET(PathPythonMainGui_SRCS Path/Main/Gui/__init__.py Path/Main/Gui/Camotics.py + Path/Main/Gui/Editor.py Path/Main/Gui/Fixture.py Path/Main/Gui/Inspect.py Path/Main/Gui/Job.py diff --git a/src/Mod/CAM/Path/Main/Gui/Editor.py b/src/Mod/CAM/Path/Main/Gui/Editor.py new file mode 100644 index 0000000000..1565a93080 --- /dev/null +++ b/src/Mod/CAM/Path/Main/Gui/Editor.py @@ -0,0 +1,83 @@ +from PySide.QtWidgets import QWidget, QPlainTextEdit +from PySide.QtGui import QPainter, QFont +from PySide.QtCore import Qt, QRect, QSize + + +class LineNumberArea(QWidget): + def __init__(self, editor): + super().__init__(editor) + self.editor = editor + + def sizeHint(self): + return QSize(self.editor.fontMetrics().horizontalAdvance("999") + 4, 0) + + def paintEvent(self, event): + painter = QPainter(self) + painter.fillRect(event.rect(), Qt.black) + font = QFont() + font.setFamily(self.editor.font().family()) + font.setFixedPitch(self.editor.font().fixedPitch()) + font.setPointSize(self.editor.font().pointSize()) + painter.setFont(font) + + block = self.editor.firstVisibleBlock() + blockNumber = block.blockNumber() + top = self.editor.blockBoundingGeometry(block).translated(self.editor.contentOffset()).top() + bottom = top + self.editor.blockBoundingRect(block).height() + + while block.isValid() and top <= event.rect().bottom(): + if block.isVisible() and bottom >= event.rect().top(): + number = str(blockNumber + 1) + painter.setPen(Qt.gray) + painter.drawText( + -6, + int(top), + self.width(), + self.editor.fontMetrics().height(), + Qt.AlignRight, + number, + ) + + block = block.next() + top = bottom + bottom = top + self.editor.blockBoundingRect(block).height() + blockNumber += 1 + + +class CodeEditor(QPlainTextEdit): + def __init__(self, parent=None): + super().__init__(parent) + self.lineNumberArea = LineNumberArea(self) + self.blockCountChanged.connect(self.updateLineNumberAreaWidth) + self.updateRequest.connect(self.updateLineNumberArea) + self.verticalScrollBar().valueChanged.connect(self.lineNumberArea.update) # Simple update + + self.updateLineNumberAreaWidth(0) # Initial call + + def lineNumberAreaWidth(self): + digits = 2 + max_value = max(1, self.blockCount()) + while max_value >= 10: + max_value /= 10 + digits += 1 + space = 3 + self.fontMetrics().horizontalAdvance("9") * digits + return space + + def updateLineNumberAreaWidth(self, newBlockCount): + self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) + + def updateLineNumberArea(self, rect, dy): + if dy: + self.lineNumberArea.scroll(0, dy) + else: + self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) + + if rect.contains(self.viewport().rect()): + self.updateLineNumberAreaWidth(0) + + def resizeEvent(self, event): + super().resizeEvent(event) + cr = self.contentsRect() + self.lineNumberArea.setGeometry( + QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()) + ) diff --git a/src/Mod/CAM/Path/Post/Utils.py b/src/Mod/CAM/Path/Post/Utils.py index 020c7552a9..01d9b632fa 100644 --- a/src/Mod/CAM/Path/Post/Utils.py +++ b/src/Mod/CAM/Path/Post/Utils.py @@ -30,13 +30,16 @@ These are common functions and classes for creating custom post processors. from Path.Base.MachineState import MachineState +from Path.Main.Gui.Editor import CodeEditor + from PySide import QtCore, QtGui + import FreeCAD -import Part import Path import os import re + debug = False if debug: Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule()) @@ -215,14 +218,16 @@ class GCodeEditorDialog(QtGui.QDialog): layout = QtGui.QVBoxLayout(self) - # nice text editor widget for editing the gcode - self.editor = QtGui.QTextEdit() + # self.editor = QtGui.QTextEdit() # without lines enumeration + self.editor = CodeEditor() # with lines enumeration + + p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Editor") font = QtGui.QFont() - font.setFamily("Courier") + font.setFamily(p.GetString("Font", "Courier")) font.setFixedPitch(True) - font.setPointSize(10) + font.setPointSize(p.GetInt("FontSize", 10)) self.editor.setFont(font) - self.editor.setText("G01 X55 Y4.5 F300.0") + self.editor.setPlainText("G01 X55 Y4.5 F300.0") layout.addWidget(self.editor) # OK and Cancel buttons diff --git a/src/Mod/CAM/Path/Post/UtilsExport.py b/src/Mod/CAM/Path/Post/UtilsExport.py index 1a503978c4..b8bf96785f 100644 --- a/src/Mod/CAM/Path/Post/UtilsExport.py +++ b/src/Mod/CAM/Path/Post/UtilsExport.py @@ -338,12 +338,12 @@ def export_common(values: Values, objectslist, filename: str) -> str: dia = PostUtils.GCodeEditorDialog() # 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.editor.setPlainText(final) if dia.exec_(): final = dia.editor.toPlainText() else: final_for_editor = "\n".join(gcode) - dia.editor.setText(final_for_editor) + dia.editor.setPlainText(final_for_editor) if dia.exec_(): final_for_editor = dia.editor.toPlainText() # convert all "\n" to the appropriate end-of-line characters diff --git a/src/Mod/CAM/Path/Post/scripts/KineticNCBeamicon2_post.py b/src/Mod/CAM/Path/Post/scripts/KineticNCBeamicon2_post.py index 4e65a33532..794bb751d9 100644 --- a/src/Mod/CAM/Path/Post/scripts/KineticNCBeamicon2_post.py +++ b/src/Mod/CAM/Path/Post/scripts/KineticNCBeamicon2_post.py @@ -259,7 +259,7 @@ def export(objectslist, filename, argstring): if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/centroid_legacy_post.py b/src/Mod/CAM/Path/Post/scripts/centroid_legacy_post.py index 1c33671f40..d5e4de441c 100644 --- a/src/Mod/CAM/Path/Post/scripts/centroid_legacy_post.py +++ b/src/Mod/CAM/Path/Post/scripts/centroid_legacy_post.py @@ -225,7 +225,7 @@ def export(objectslist, filename, argstring): if SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/dynapath_4060_post.py b/src/Mod/CAM/Path/Post/scripts/dynapath_4060_post.py index 729da0aad6..9770c3c8df 100644 --- a/src/Mod/CAM/Path/Post/scripts/dynapath_4060_post.py +++ b/src/Mod/CAM/Path/Post/scripts/dynapath_4060_post.py @@ -292,7 +292,7 @@ def export(objectslist, filename, argstring): if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/dynapath_post.py b/src/Mod/CAM/Path/Post/scripts/dynapath_post.py index 4e7d0f6741..129d80f08a 100644 --- a/src/Mod/CAM/Path/Post/scripts/dynapath_post.py +++ b/src/Mod/CAM/Path/Post/scripts/dynapath_post.py @@ -263,7 +263,7 @@ def export(objectslist, filename, argstring): print("show editor: {}".format(SHOW_EDITOR)) if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/estlcam_post.py b/src/Mod/CAM/Path/Post/scripts/estlcam_post.py index a5a415478d..9383c8bdca 100644 --- a/src/Mod/CAM/Path/Post/scripts/estlcam_post.py +++ b/src/Mod/CAM/Path/Post/scripts/estlcam_post.py @@ -312,7 +312,7 @@ def export(objectslist, filename, argstring): # show the gCode result dialog if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/fablin_post.py b/src/Mod/CAM/Path/Post/scripts/fablin_post.py index 4297dcead0..e3abbbd040 100644 --- a/src/Mod/CAM/Path/Post/scripts/fablin_post.py +++ b/src/Mod/CAM/Path/Post/scripts/fablin_post.py @@ -190,7 +190,7 @@ def export(objectslist, filename, argstring): if SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/fangling_post.py b/src/Mod/CAM/Path/Post/scripts/fangling_post.py index 85aeea8100..938968ebc9 100644 --- a/src/Mod/CAM/Path/Post/scripts/fangling_post.py +++ b/src/Mod/CAM/Path/Post/scripts/fangling_post.py @@ -255,7 +255,7 @@ def export(objectslist, filename, argstring): if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) if dia.exec_(): final = dia.editor.toPlainText() else: diff --git a/src/Mod/CAM/Path/Post/scripts/fanuc_post.py b/src/Mod/CAM/Path/Post/scripts/fanuc_post.py index 85a1c2b741..f16c27af16 100644 --- a/src/Mod/CAM/Path/Post/scripts/fanuc_post.py +++ b/src/Mod/CAM/Path/Post/scripts/fanuc_post.py @@ -276,7 +276,7 @@ def export(objectslist, filename, argstring): if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/grbl_legacy_post.py b/src/Mod/CAM/Path/Post/scripts/grbl_legacy_post.py index d3b6b96f25..694e078f92 100644 --- a/src/Mod/CAM/Path/Post/scripts/grbl_legacy_post.py +++ b/src/Mod/CAM/Path/Post/scripts/grbl_legacy_post.py @@ -370,7 +370,7 @@ def export(objectslist, filename, argstring): # show the gCode result dialog if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/jtech_post.py b/src/Mod/CAM/Path/Post/scripts/jtech_post.py index 33f8fdbd53..f84309b223 100644 --- a/src/Mod/CAM/Path/Post/scripts/jtech_post.py +++ b/src/Mod/CAM/Path/Post/scripts/jtech_post.py @@ -228,7 +228,7 @@ def export(objectslist, filename, argstring): if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/linuxcnc_legacy_post.py b/src/Mod/CAM/Path/Post/scripts/linuxcnc_legacy_post.py index acd2801c01..ef132ca17a 100644 --- a/src/Mod/CAM/Path/Post/scripts/linuxcnc_legacy_post.py +++ b/src/Mod/CAM/Path/Post/scripts/linuxcnc_legacy_post.py @@ -252,7 +252,7 @@ def export(objectslist, filename, argstring): print("Skipping editor since output is greater than 100kb") else: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/mach3_mach4_legacy_post.py b/src/Mod/CAM/Path/Post/scripts/mach3_mach4_legacy_post.py index 0353a1f21d..a2f7952b87 100644 --- a/src/Mod/CAM/Path/Post/scripts/mach3_mach4_legacy_post.py +++ b/src/Mod/CAM/Path/Post/scripts/mach3_mach4_legacy_post.py @@ -252,7 +252,7 @@ def export(objectslist, filename, argstring): if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/marlin_post.py b/src/Mod/CAM/Path/Post/scripts/marlin_post.py index ed0ff90851..cc7e29fb65 100644 --- a/src/Mod/CAM/Path/Post/scripts/marlin_post.py +++ b/src/Mod/CAM/Path/Post/scripts/marlin_post.py @@ -440,7 +440,7 @@ def export(objectslist, filename, argstring): # Show the gcode result dialog: if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/nccad_post.py b/src/Mod/CAM/Path/Post/scripts/nccad_post.py index e1cd317c05..155e88d591 100644 --- a/src/Mod/CAM/Path/Post/scripts/nccad_post.py +++ b/src/Mod/CAM/Path/Post/scripts/nccad_post.py @@ -124,7 +124,7 @@ def export(objectslist, filename, argstring): # Open editor window if FreeCAD.GuiUp: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: gcode = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/opensbp_post.py b/src/Mod/CAM/Path/Post/scripts/opensbp_post.py index 06a87ae532..df3b375a05 100644 --- a/src/Mod/CAM/Path/Post/scripts/opensbp_post.py +++ b/src/Mod/CAM/Path/Post/scripts/opensbp_post.py @@ -171,7 +171,7 @@ def export(objectslist, filename, argstring): if SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/rrf_post.py b/src/Mod/CAM/Path/Post/scripts/rrf_post.py index 7c882c4f6e..74e5d8d725 100644 --- a/src/Mod/CAM/Path/Post/scripts/rrf_post.py +++ b/src/Mod/CAM/Path/Post/scripts/rrf_post.py @@ -433,7 +433,7 @@ def export(objectslist, filename, argstring): # Show the gcode result dialog: if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/snapmaker_post.py b/src/Mod/CAM/Path/Post/scripts/snapmaker_post.py index a7e5a19df9..e49ea7bbc4 100644 --- a/src/Mod/CAM/Path/Post/scripts/snapmaker_post.py +++ b/src/Mod/CAM/Path/Post/scripts/snapmaker_post.py @@ -815,12 +815,12 @@ class Snapmaker(Path.Post.Processor.PostProcessor): dia = PostUtils.GCodeEditorDialog() # the editor expects lines to end in "\n", and returns lines ending in "\n" if self.values["END_OF_LINE_CHARACTERS"] == "\n": - dia.editor.setText(final) + dia.editor.setPlainText(final) if dia.exec_(): final = dia.editor.toPlainText() else: final_for_editor = "\n".join(gcode) - dia.editor.setText(final_for_editor) + dia.editor.setPlainText(final_for_editor) if dia.exec_(): final_for_editor = dia.editor.toPlainText() # convert all "\n" to the appropriate end-of-line characters diff --git a/src/Mod/CAM/Path/Post/scripts/uccnc_post.py b/src/Mod/CAM/Path/Post/scripts/uccnc_post.py index 6018821896..0743ddfd01 100644 --- a/src/Mod/CAM/Path/Post/scripts/uccnc_post.py +++ b/src/Mod/CAM/Path/Post/scripts/uccnc_post.py @@ -507,7 +507,7 @@ def export(objectslist, filename, argstring): # Show the results if SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText() diff --git a/src/Mod/CAM/Path/Post/scripts/wedm_post.py b/src/Mod/CAM/Path/Post/scripts/wedm_post.py index 486df89d2b..0d56451bf7 100644 --- a/src/Mod/CAM/Path/Post/scripts/wedm_post.py +++ b/src/Mod/CAM/Path/Post/scripts/wedm_post.py @@ -356,7 +356,7 @@ def export(objectslist, filename, argstring): print("Skipping editor since output is greater than 100kb") else: dia = PostUtils.GCodeEditorDialog() - dia.editor.setText(gcode) + dia.editor.setPlainText(gcode) result = dia.exec_() if result: final = dia.editor.toPlainText()