| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | """Post Process command that will make use of the Output File and Post |
| | Processor entries in PathJob""" |
| |
|
| |
|
| | import FreeCAD |
| | import FreeCADGui |
| | import Path |
| | from PathScripts import PathUtils |
| | from Path.Post.Utils import FilenameGenerator |
| | import os |
| | from Path.Post.Processor import PostProcessor, PostProcessorFactory |
| | from PySide import QtCore, QtGui |
| | from PySide.QtCore import QT_TRANSLATE_NOOP |
| |
|
| | LOG_MODULE = Path.Log.thisModule() |
| |
|
| | DEBUG = False |
| | if DEBUG: |
| | Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule()) |
| | Path.Log.trackModule(Path.Log.thisModule()) |
| | else: |
| | Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) |
| |
|
| |
|
| | translate = FreeCAD.Qt.translate |
| |
|
| |
|
| | def _resolve_post_processor_name(job): |
| | Path.Log.debug("_resolve_post_processor_name()") |
| | if job.PostProcessor: |
| | valid_name = job.PostProcessor |
| | elif Path.Preferences.defaultPostProcessor(): |
| | valid_name = Path.Preferences.defaultPostProcessor() |
| | elif FreeCAD.GuiUp: |
| | valid_name = DlgSelectPostProcessor().exec_() |
| | else: |
| | valid_name = None |
| |
|
| | if valid_name and PostProcessor.exists(valid_name): |
| | return valid_name |
| | raise ValueError("Post processor not identified.") |
| |
|
| |
|
| | class DlgSelectPostProcessor: |
| | """Provide user with list of available and active post processor |
| | choices.""" |
| |
|
| | def __init__(self): |
| | self.dialog = FreeCADGui.PySideUic.loadUi(":/panels/DlgSelectPostProcessor.ui") |
| | firstItem = None |
| | for post in Path.Preferences.allEnabledPostProcessors(): |
| | item = QtGui.QListWidgetItem(post) |
| | item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) |
| | self.dialog.lwPostProcessor.addItem(item) |
| | if not firstItem: |
| | firstItem = item |
| | if firstItem: |
| | self.dialog.lwPostProcessor.setCurrentItem(firstItem) |
| | else: |
| | self.dialog.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(False) |
| | self.tooltips = {} |
| | self.dialog.lwPostProcessor.itemDoubleClicked.connect(self.dialog.accept) |
| | self.dialog.lwPostProcessor.setMouseTracking(True) |
| | self.dialog.lwPostProcessor.itemEntered.connect(self.updateTooltip) |
| |
|
| | def updateTooltip(self, item): |
| | if item.text() in self.tooltips: |
| | tooltip = self.tooltips[item.text()] |
| | else: |
| | processor = PostProcessor.load(item.text()) |
| | self.tooltips[item.text()] = processor.tooltip |
| | tooltip = processor.tooltip |
| | self.dialog.lwPostProcessor.setToolTip(tooltip) |
| |
|
| | def exec_(self): |
| | if self.dialog.exec_() == 1: |
| | posts = self.dialog.lwPostProcessor.selectedItems() |
| | return posts[0].text() |
| | return None |
| |
|
| |
|
| | class CommandPathPost: |
| | def GetResources(self): |
| | return { |
| | "Pixmap": "CAM_Post", |
| | "MenuText": QT_TRANSLATE_NOOP("CAM_Post", "Post Process"), |
| | "Accel": "P, P", |
| | "ToolTip": QT_TRANSLATE_NOOP("CAM_Post", "Post Processes the selected Job"), |
| | } |
| |
|
| | def IsActive(self): |
| | selection = FreeCADGui.Selection.getSelection() |
| | if not selection: |
| | return False |
| |
|
| | self.candidate = PathUtils.findParentJob(selection[0]) |
| |
|
| | return self.candidate is not None |
| |
|
| | def _write_file(self, filename, gcode, policy): |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if len(gcode) > 1 and gcode[0:2] == "\n\n": |
| | |
| | |
| | |
| | newline_handling = "" |
| | gcode = gcode[2:] |
| | elif "\r" in gcode: |
| | |
| | |
| | newline_handling = "" |
| | else: |
| | |
| | |
| | |
| | |
| | |
| | newline_handling = None |
| |
|
| | if policy.casefold() == "open file dialog": |
| | dlg = QtGui.QFileDialog() |
| | dlg.setFileMode(QtGui.QFileDialog.FileMode.AnyFile) |
| | dlg.setAcceptMode(QtGui.QFileDialog.AcceptMode.AcceptSave) |
| | dlg.setDirectory(os.path.dirname(filename)) |
| | dlg.selectFile(os.path.basename(filename)) |
| | if dlg.exec_(): |
| | filename = dlg.selectedFiles()[0] |
| | Path.Log.debug(filename) |
| | with open(filename, "w", encoding="utf-8", newline=newline_handling) as f: |
| | f.write(gcode) |
| | else: |
| | return |
| |
|
| | elif policy.casefold() == "append unique id on conflict": |
| | while os.path.isfile(filename): |
| | base, ext = os.path.splitext(filename) |
| | filename = f"{base}-1{ext}" |
| | with open(filename, "w", encoding="utf-8", newline=newline_handling) as f: |
| | f.write(gcode) |
| |
|
| | elif policy.casefold() == "open file dialog on conflict": |
| | if os.path.isfile(filename): |
| | dlg = QtGui.QFileDialog() |
| | dlg.setFileMode(QtGui.QFileDialog.FileMode.AnyFile) |
| | dlg.setAcceptMode(QtGui.QFileDialog.AcceptSave) |
| | dlg.setDirectory(os.path.dirname(filename)) |
| | dlg.selectFile(os.path.basename(filename)) |
| | if dlg.exec_(): |
| | filename = dlg.selectedFiles()[0] |
| | Path.Log.debug(filename) |
| | with open(filename, "w", encoding="utf-8", newline=newline_handling) as f: |
| | f.write(gcode) |
| | else: |
| | return |
| | else: |
| | with open(filename, "w", encoding="utf-8", newline=newline_handling) as f: |
| | f.write(gcode) |
| |
|
| | else: |
| | with open(filename, "w", encoding="utf-8", newline=newline_handling) as f: |
| | f.write(gcode) |
| |
|
| | FreeCAD.Console.PrintMessage(f"File written to {filename}\n") |
| |
|
| | def Activated(self): |
| | """ |
| | Handles the activation of post processing, initiating the process based |
| | on user selection and document context. |
| | """ |
| | Path.Log.debug(self.candidate.Name) |
| | FreeCAD.ActiveDocument.openTransaction("Post Process the Selected Job") |
| |
|
| | postprocessor_name = _resolve_post_processor_name(self.candidate) |
| | Path.Log.debug(f"Post Processor: {postprocessor_name}") |
| |
|
| | if not postprocessor_name: |
| | FreeCAD.ActiveDocument.abortTransaction() |
| | return |
| |
|
| | |
| | postprocessor = PostProcessorFactory.get_post_processor( |
| | self.candidate, |
| | postprocessor_name, |
| | ) |
| |
|
| | post_data = postprocessor.export() |
| | |
| | |
| | if not post_data: |
| | FreeCAD.ActiveDocument.abortTransaction() |
| | return |
| |
|
| | policy = Path.Preferences.defaultOutputPolicy() |
| | generator = FilenameGenerator(job=self.candidate) |
| | generated_filename = generator.generate_filenames() |
| |
|
| | for item in post_data: |
| | subpart, gcode = item |
| |
|
| | |
| | subpart = "" if subpart == "allitems" else subpart |
| | Path.Log.debug(subpart) |
| | generator.set_subpartname(subpart) |
| | fname = next(generated_filename) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if gcode is not None: |
| | |
| | self._write_file(fname, gcode, policy) |
| |
|
| | FreeCAD.ActiveDocument.commitTransaction() |
| | FreeCAD.ActiveDocument.recompute() |
| |
|
| |
|
| | class CommandPathPostSelected(CommandPathPost): |
| | def GetResources(self): |
| | return { |
| | "Pixmap": "CAM_PostSelected", |
| | "MenuText": QT_TRANSLATE_NOOP("CAM_Post", "Post Process Selected"), |
| | "Accel": "P, O", |
| | "ToolTip": QT_TRANSLATE_NOOP("CAM_Post", "Post Processes the selected operations"), |
| | } |
| |
|
| | def IsActive(self): |
| | selection = FreeCADGui.Selection.getSelection() |
| | if not selection: |
| | return False |
| |
|
| | return all(hasattr(op, "Path") and not op.Name.startswith("Job") for op in selection) |
| |
|
| | def Activated(self): |
| | """ |
| | Handles the activation of post processing, initiating the process based |
| | on user selection and document context. |
| | """ |
| | FreeCAD.ActiveDocument.openTransaction("Post Process the Selected operations") |
| |
|
| | selection = FreeCADGui.Selection.getSelection() |
| | job = PathUtils.findParentJob(selection[0]) |
| | if ( |
| | not job |
| | and hasattr(selection[0], "Base") |
| | and isinstance(selection[0].Base, list) |
| | and selection[0].Base |
| | ): |
| | |
| | baseOp = FreeCAD.ActiveDocument.getObject(selection[0].Base[0]) |
| | job = PathUtils.findParentJob(baseOp) |
| |
|
| | opCandidates = [op for op in selection if hasattr(op, "Path") and "Job" not in op.Name] |
| | operations = [] |
| | if opCandidates and job.Operations.Group != opCandidates: |
| | msgBox = QtGui.QMessageBox() |
| | msgBox.setWindowTitle("Post Process") |
| | msgBox.setText("<p align='center'>What needs to be exported?</p>") |
| | msgBox.setInformativeText( |
| | "<p align='center'>Check to make sure that you won't break anything by leaving out operations</p>" |
| | ) |
| | msgBox.findChild(QtGui.QGridLayout).setColumnMinimumWidth(1, 250) |
| | btn1 = msgBox.addButton("Only selected", QtGui.QMessageBox.ButtonRole.YesRole) |
| | btn2 = msgBox.addButton("All", QtGui.QMessageBox.ButtonRole.NoRole) |
| | msgBox.setDefaultButton(btn2) |
| | msgBox.exec() |
| |
|
| | if msgBox.clickedButton() == btn1: |
| | print( |
| | f"Post process only selected operations: {', '.join([op.Name for op in opCandidates])}" |
| | ) |
| | operations = opCandidates |
| |
|
| | postprocessor_name = _resolve_post_processor_name(job) |
| | Path.Log.debug(f"Post Processor: {postprocessor_name}") |
| |
|
| | if not postprocessor_name: |
| | FreeCAD.ActiveDocument.abortTransaction() |
| | return |
| |
|
| | |
| | postprocessor = PostProcessorFactory.get_post_processor( |
| | {"job": job, "operations": operations}, postprocessor_name |
| | ) |
| |
|
| | post_data = postprocessor.export() |
| | |
| | |
| | if not post_data: |
| | FreeCAD.ActiveDocument.abortTransaction() |
| | return |
| |
|
| | policy = Path.Preferences.defaultOutputPolicy() |
| | generator = FilenameGenerator(job=job) |
| | generated_filename = generator.generate_filenames() |
| |
|
| | for item in post_data: |
| | subpart, gcode = item |
| |
|
| | |
| | subpart = "" if subpart == "allitems" else subpart |
| | Path.Log.debug(subpart) |
| | generator.set_subpartname(subpart) |
| | fname = next(generated_filename) |
| |
|
| | if gcode is not None: |
| | |
| | self._write_file(fname, gcode, policy) |
| |
|
| | FreeCAD.ActiveDocument.commitTransaction() |
| | FreeCAD.ActiveDocument.recompute() |
| |
|
| |
|
| | if FreeCAD.GuiUp: |
| | |
| | FreeCADGui.addCommand("CAM_Post", CommandPathPost()) |
| | FreeCADGui.addCommand("CAM_PostSelected", CommandPathPostSelected()) |
| |
|
| | FreeCAD.Console.PrintLog("Loading PathPost… done\n") |
| |
|