From 071bdcc5790e3a4747bd429674bd126f6074dd03 Mon Sep 17 00:00:00 2001
From: Furgo <148809153+furgo16@users.noreply.github.com>
Date: Sun, 29 Jun 2025 08:53:38 +0200
Subject: [PATCH] Import: DXF, add dedicated import dialog
---
src/Mod/Draft/CMakeLists.txt | 1 +
src/Mod/Draft/DxfImportDialog.py | 116 ++++++++++
src/Mod/Draft/Resources/Draft.qrc | 1 +
.../Resources/ui/preferences-dxf-import.ui | 154 +++++++++++++
src/Mod/Draft/importDXF.py | 213 ++++++++++++------
src/Mod/Import/App/dxf/ImpExpDxf.cpp | 30 +++
src/Mod/Import/App/dxf/ImpExpDxf.h | 2 +-
src/Mod/Import/Gui/AppImportGuiPy.cpp | 21 ++
8 files changed, 462 insertions(+), 76 deletions(-)
create mode 100644 src/Mod/Draft/DxfImportDialog.py
create mode 100644 src/Mod/Draft/Resources/ui/preferences-dxf-import.ui
diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt
index 7e08b0c53e..f985c4350c 100644
--- a/src/Mod/Draft/CMakeLists.txt
+++ b/src/Mod/Draft/CMakeLists.txt
@@ -20,6 +20,7 @@ SET(Draft_SRCS_base
SET(Draft_import
importAirfoilDAT.py
importDXF.py
+ DxfImportDialog.py
importDWG.py
importOCA.py
importSVG.py
diff --git a/src/Mod/Draft/DxfImportDialog.py b/src/Mod/Draft/DxfImportDialog.py
new file mode 100644
index 0000000000..a1c7fec800
--- /dev/null
+++ b/src/Mod/Draft/DxfImportDialog.py
@@ -0,0 +1,116 @@
+import FreeCAD
+import FreeCADGui
+from PySide import QtCore, QtGui
+
+class DxfImportDialog:
+ """
+ A controller class that creates, manages, and shows the DXF import dialog.
+ """
+ def __init__(self, entity_counts, parent=None):
+ # Step 1: Load the UI from the resource file. This returns a new QDialog instance.
+ self.dialog = FreeCADGui.PySideUic.loadUi(":/ui/preferences-dxf-import.ui")
+
+ # Now, all widgets like "label_Summary" are attributes of self.dialog
+
+ self.entity_counts = entity_counts
+ self.total_entities = sum(entity_counts.values())
+
+ self.setup_ui()
+ self.connect_signals()
+ self.load_settings_and_set_initial_state()
+
+ def setup_ui(self):
+ """Perform initial UI setup."""
+ self.dialog.label_Summary.setText(f"File contains approximately {self.total_entities} geometric entities.")
+ self.dialog.label_Warning.hide()
+
+ def connect_signals(self):
+ """Connect signals from the dialog's widgets to our methods."""
+ buttonBox = self.dialog.findChild(QtGui.QDialogButtonBox, "buttonBox")
+ if buttonBox:
+ # Connect to our custom slots INSTEAD of the dialog's built-in ones
+ buttonBox.accepted.connect(self.on_accept)
+ buttonBox.rejected.connect(self.on_reject)
+ FreeCAD.Console.PrintLog("DxfImportDialog: OK and Cancel buttons connected.\n")
+ else:
+ FreeCAD.Console.PrintWarning("DxfImportDialog: Could not find buttonBox!\n")
+
+ self.dialog.radio_ImportAs_Draft.toggled.connect(self.update_warning_label)
+ self.dialog.radio_ImportAs_Primitives.toggled.connect(self.update_warning_label)
+ self.dialog.radio_ImportAs_Shapes.toggled.connect(self.update_warning_label)
+ self.dialog.radio_ImportAs_Fused.toggled.connect(self.update_warning_label)
+
+ def on_accept(self):
+ """Custom slot to debug the OK button click."""
+ FreeCAD.Console.PrintLog("DxfImportDialog: 'OK' button clicked. Calling self.dialog.accept().\n")
+ # Manually call the original slot
+ self.dialog.accept()
+ FreeCAD.Console.PrintLog("DxfImportDialog: self.dialog.accept() has been called.\n")
+
+ def on_reject(self):
+ """Custom slot to debug the Cancel button click."""
+ FreeCAD.Console.PrintLog("DxfImportDialog: 'Cancel' button clicked. Calling self.dialog.reject().\n")
+ # Manually call the original slot
+ self.dialog.reject()
+ FreeCAD.Console.PrintLog("DxfImportDialog: self.dialog.reject() has been called.\n")
+
+ def load_settings_and_set_initial_state(self):
+ """Load saved preferences and set the initial state of the dialog."""
+ hGrp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
+
+ mode = hGrp.GetInt("DxfImportMode", 2)
+
+ if mode == 0:
+ self.dialog.radio_ImportAs_Draft.setChecked(True)
+ elif mode == 1:
+ self.dialog.radio_ImportAs_Primitives.setChecked(True)
+ elif mode == 3:
+ self.dialog.radio_ImportAs_Fused.setChecked(True)
+ else:
+ self.dialog.radio_ImportAs_Shapes.setChecked(True)
+
+ is_legacy = hGrp.GetBool("dxfUseLegacyImporter", False)
+ if is_legacy:
+ self.dialog.radio_ImportAs_Primitives.setEnabled(False)
+ self.dialog.radio_ImportAs_Draft.setEnabled(True)
+ self.dialog.radio_ImportAs_Shapes.setEnabled(True)
+ self.dialog.radio_ImportAs_Fused.setEnabled(True)
+ else:
+ self.dialog.radio_ImportAs_Draft.setEnabled(False)
+ self.dialog.radio_ImportAs_Primitives.setEnabled(False)
+ self.dialog.radio_ImportAs_Shapes.setEnabled(True)
+ self.dialog.radio_ImportAs_Fused.setEnabled(True)
+
+ self.update_warning_label()
+
+ def update_warning_label(self):
+ """Updates the warning label based on selection and entity count."""
+ self.dialog.label_Warning.hide()
+ current_mode = self.get_selected_mode()
+
+ if self.total_entities > 5000 and (current_mode == 0 or current_mode == 1):
+ self.dialog.label_Warning.setText("Warning: Importing over 5000 entities as editable objects can be very slow.")
+ self.dialog.label_Warning.show()
+ elif self.total_entities > 20000 and current_mode == 2:
+ self.dialog.label_Warning.setText("Warning: Importing over 20,000 entities as individual shapes may be slow.")
+ self.dialog.label_Warning.show()
+
+ def exec_(self):
+ FreeCAD.Console.PrintLog("DxfImportDialog: Calling self.dialog.exec_()...\n")
+ result = self.dialog.exec_()
+ FreeCAD.Console.PrintLog("DxfImportDialog: self.dialog.exec_() returned with result: {}\n".format(result))
+ # QDialog.Accepted is usually 1, Rejected is 0.
+ FreeCAD.Console.PrintLog("(Note: QDialog.Accepted = {}, QDialog.Rejected = {})\n".format(QtGui.QDialog.Accepted, QtGui.QDialog.Rejected))
+ return result
+
+ def get_selected_mode(self):
+ """Return the integer value of the selected import mode."""
+ if self.dialog.radio_ImportAs_Draft.isChecked(): return 0
+ if self.dialog.radio_ImportAs_Primitives.isChecked(): return 1
+ if self.dialog.radio_ImportAs_Fused.isChecked(): return 3
+ if self.dialog.radio_ImportAs_Shapes.isChecked(): return 2
+ return 2
+
+ def get_show_dialog_again(self):
+ """Return True if the dialog should be shown next time."""
+ return not self.dialog.checkBox_ShowDialogAgain.isChecked()
diff --git a/src/Mod/Draft/Resources/Draft.qrc b/src/Mod/Draft/Resources/Draft.qrc
index 8cc66d2943..fa6211ca12 100644
--- a/src/Mod/Draft/Resources/Draft.qrc
+++ b/src/Mod/Draft/Resources/Draft.qrc
@@ -185,6 +185,7 @@
ui/preferences-draftvisual.ui
ui/preferences-dwg.ui
ui/preferences-dxf.ui
+ ui/preferences-dxf-import.ui
ui/preferences-oca.ui
ui/preferences-svg.ui
ui/TaskPanel_CircularArray.ui
diff --git a/src/Mod/Draft/Resources/ui/preferences-dxf-import.ui b/src/Mod/Draft/Resources/ui/preferences-dxf-import.ui
new file mode 100644
index 0000000000..fa66b241e8
--- /dev/null
+++ b/src/Mod/Draft/Resources/ui/preferences-dxf-import.ui
@@ -0,0 +1,154 @@
+
+
+ DxfImportDialog
+
+
+
+ 0
+ 0
+ 480
+ 280
+
+
+
+ DXF Import
+
+
+ -
+
+
+ Import as
+
+
+
-
+
+
+ Creates fully parametric Draft objects. Block definitions are imported as
+reusable objects (Part Compounds) and instances become `App::Link` objects,
+maintaining the block structure. Best for full integration with the Draft
+Workbench. (Legacy importer only)
+
+
+ Editable draft objects
+
+
+
+ -
+
+
+ Creates parametric Part objects (e.g., Part::Line, Part::Circle). Block
+definitions are imported as reusable objects (Part Compounds) and instances
+become `App::Link` objects, maintaining the block structure. Best for
+script-based post-processing. (Not yet implemented)
+
+
+ Editable part primitives
+
+
+
+ -
+
+
+ Creates a non-parametric shape for each DXF entity. Block definitions are
+imported as reusable objects (Part Compounds) and instances become `App::Link`
+objects, maintaining the block structure. Good for referencing and measuring.
+
+
+ Individual part shapes (recommended)
+
+
+
+ -
+
+
+ Merges all geometry per layer into a single, non-editable shape. Block
+structures are not preserved; their geometry becomes part of the layer's
+shape. Best for viewing very large files with maximum performance.
+
+
+ Fused part shapes (fastest)
+
+
+
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
+
-
+
+
+ File summary
+
+
+
+ -
+
+
+ color: #c00;
+
+
+ Warning
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 0
+
+
+
+
+ -
+
+
+ Do not show this dialog again
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Mod/Draft/importDXF.py b/src/Mod/Draft/importDXF.py
index 39fcc9c3a9..cc67c5292b 100644
--- a/src/Mod/Draft/importDXF.py
+++ b/src/Mod/Draft/importDXF.py
@@ -69,6 +69,7 @@ from draftobjects.dimension import _Dimension
from draftutils import params
from draftutils import utils
from draftutils.utils import pyopen
+from PySide import QtCore, QtGui
gui = FreeCAD.GuiUp
draftui = None
@@ -2810,10 +2811,51 @@ def open(filename):
-----
Use local variables, not global variables.
"""
- readPreferences()
+ hGrp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
+ use_legacy = hGrp.GetBool("dxfUseLegacyImporter", False)
+
+ # The C++ layer (`Gui::Application::importFrom`) has set the WaitCursor.
+ # We must temporarily suspend it to show our interactive dialog.
+ try:
+ if gui:
+ FreeCADGui.suspendWaitCursor()
+
+ # --- Dialog Workflow ---
+ if gui and not use_legacy and hGrp.GetBool("dxfShowDialog", True):
+ try:
+ import ImportGui
+ # This C++ function will need to be created in a later step
+ entity_counts = ImportGui.preScanDxf(filename)
+ except Exception:
+ entity_counts = {}
+
+ from DxfImportDialog import DxfImportDialog
+ dlg = DxfImportDialog(entity_counts)
+
+ if dlg.exec_():
+ # User clicked OK, save settings from the dialog
+ hGrp.SetInt("DxfImportMode", dlg.get_selected_mode())
+ hGrp.SetBool("dxfShowDialog", dlg.get_show_dialog_again())
+ else:
+ # User clicked Cancel, abort the entire operation
+ FCC.PrintLog("DXF import cancelled by user.\n")
+ return
+ else:
+ # If we don't show the dialog, we still need to read preferences
+ # to ensure the correct backend logic is triggered.
+ readPreferences()
+
+ finally:
+ # --- CRITICAL: Always resume the wait state before returning to C++ ---
+ # This restores the wait cursor and event filter so the subsequent
+ # blocking C++ call behaves as expected within the C++ scope.
+ if gui:
+ FreeCADGui.resumeWaitCursor()
+
+ # --- Proceed with the blocking import logic ---
total_start_time = time.perf_counter()
- if dxfUseLegacyImporter:
+ if use_legacy:
getDXFlibs()
if dxfReader:
docname = os.path.splitext(os.path.basename(filename))[0]
@@ -2823,12 +2865,14 @@ def open(filename):
return doc
else:
errorDXFLib(gui)
- else:
+ return None
+ else: # Modern C++ Importer
docname = os.path.splitext(os.path.basename(filename))[0]
doc = FreeCAD.newDocument(docname)
doc.Label = docname
FreeCAD.setActiveDocument(doc.Name)
stats = None
+
if gui:
import ImportGui
stats = ImportGui.readDXF(filename)
@@ -2836,13 +2880,16 @@ def open(filename):
import Import
stats = Import.readDXF(filename)
+ Draft.convert_draft_texts()
+ doc.recompute()
+
total_end_time = time.perf_counter()
if stats:
+ # Report PROCESSING time only, not user dialog time.
reporter = DxfImportReporter(filename, stats, total_end_time - total_start_time)
reporter.report_to_console()
- Draft.convert_draft_texts() # convert annotations to Draft texts
- doc.recompute()
+ return doc
def insert(filename, docname):
@@ -2864,20 +2911,53 @@ def insert(filename, docname):
-----
Use local variables, not global variables.
"""
- readPreferences()
+ hGrp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
+ use_legacy = hGrp.GetBool("dxfUseLegacyImporter", False)
+
+ try:
+ if gui:
+ FreeCADGui.suspendWaitCursor()
+
+ # --- Dialog Workflow ---
+ if gui and not use_legacy and hGrp.GetBool("dxfShowDialog", True):
+ try:
+ import ImportGui
+ entity_counts = ImportGui.preScanDxf(filename)
+ except Exception:
+ entity_counts = {}
+
+ from DxfImportDialog import DxfImportDialog
+ dlg = DxfImportDialog(entity_counts)
+
+ if dlg.exec_():
+ hGrp.SetInt("DxfImportMode", dlg.get_selected_mode())
+ hGrp.SetBool("dxfShowDialog", dlg.get_show_dialog_again())
+ else:
+ FCC.PrintLog("DXF insert cancelled by user.\n")
+ return
+ else:
+ readPreferences()
+
+ finally:
+ if gui:
+ FreeCADGui.resumeWaitCursor()
+
+ # --- Proceed with the blocking insert logic ---
total_start_time = time.perf_counter()
+
try:
doc = FreeCAD.getDocument(docname)
except NameError:
doc = FreeCAD.newDocument(docname)
FreeCAD.setActiveDocument(docname)
- if dxfUseLegacyImporter:
+
+ if use_legacy:
getDXFlibs()
if dxfReader:
processdxf(doc, filename)
else:
errorDXFLib(gui)
- else:
+ else: # Modern C++ Importer
stats = None
if gui:
import ImportGui
@@ -2886,14 +2966,14 @@ def insert(filename, docname):
import Import
stats = Import.readDXF(filename)
+ Draft.convert_draft_texts()
+ doc.recompute()
+
total_end_time = time.perf_counter()
if stats:
reporter = DxfImportReporter(filename, stats, total_end_time - total_start_time)
reporter.report_to_console()
- Draft.convert_draft_texts() # convert annotations to Draft texts
- doc.recompute()
-
def getShapes(filename):
"""Read a DXF file, and return a list of shapes from its contents.
@@ -4183,81 +4263,64 @@ def readPreferences():
-----
Use local variables, not global variables.
"""
- # reading parameters
- if gui and params.get_param("dxfShowDialog"):
- FreeCADGui.showPreferencesByName("Import-Export", ":/ui/preferences-dxf.ui")
-
global dxfCreatePart, dxfCreateDraft, dxfCreateSketch
- global dxfDiscretizeCurves, dxfStarBlocks
- global dxfMakeBlocks, dxfJoin, dxfRenderPolylineWidth
- global dxfImportTexts, dxfImportLayouts
- global dxfImportPoints, dxfImportHatches, dxfUseStandardSize
- global dxfGetColors, dxfUseDraftVisGroups
- global dxfMakeFaceMode, dxfBrightBackground, dxfDefaultColor
- global dxfUseLegacyImporter, dxfExportBlocks, dxfScaling
- global dxfUseLegacyExporter
+ global dxfDiscretizeCurves, dxfStarBlocks, dxfMakeBlocks, dxfJoin, dxfRenderPolylineWidth
+ global dxfImportTexts, dxfImportLayouts, dxfImportPoints, dxfImportHatches, dxfUseStandardSize
+ global dxfGetColors, dxfUseDraftVisGroups, dxfMakeFaceMode, dxfBrightBackground, dxfDefaultColor
+ global dxfUseLegacyImporter, dxfExportBlocks, dxfScaling, dxfUseLegacyExporter
- # --- Read all feature and appearance toggles ---
- # These are independent settings and can be read directly.
- dxfDiscretizeCurves = params.get_param("DiscretizeEllipses", False)
- dxfStarBlocks = params.get_param("dxfstarblocks", False)
- dxfJoin = params.get_param("joingeometry", False)
- dxfRenderPolylineWidth = params.get_param("renderPolylineWidth", False)
- dxfImportTexts = params.get_param("dxftext", True)
- dxfImportLayouts = params.get_param("dxflayout", False)
- dxfImportPoints = params.get_param("dxfImportPoints", True)
- dxfImportHatches = params.get_param("importDxfHatches", True)
- dxfUseStandardSize = params.get_param("dxfStdSize", False)
- dxfGetColors = params.get_param("dxfGetOriginalColors", True)
- dxfUseDraftVisGroups = params.get_param("dxfUseDraftVisGroups", True)
- dxfMakeFaceMode = params.get_param("MakeFaceMode", False)
- dxfExportBlocks = params.get_param("dxfExportBlocks", True)
- dxfScaling = params.get_param("dxfScaling", 1.0)
+ # Use the direct C++ API via Python for all parameter access
+ hGrp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
- # These control which importer is used. The script should only proceed if
- # the legacy importer is selected.
- dxfUseLegacyImporter = params.get_param("dxfUseLegacyImporter", False)
- dxfUseLegacyExporter = params.get_param("dxfUseLegacyExporter", False)
+ dxfUseLegacyImporter = hGrp.GetBool("dxfUseLegacyImporter", False)
- if not dxfUseLegacyImporter:
- # If the legacy importer is called when not selected, exit.
- # This prevents accidental execution.
- return
+ # This logic is now only needed for the legacy importer.
+ # The modern importer reads its settings directly in C++.
+ if dxfUseLegacyImporter:
+ # Legacy override for sketch creation takes highest priority
+ dxfCreateSketch = hGrp.GetBool("dxfCreateSketch", False)
- # --- New, Centralized Logic for Structural Mode ---
-
- # Read the legacy-specific override for sketch creation.
- dxfCreateSketch = params.get_param("dxfCreateSketch", False)
-
- if dxfCreateSketch:
- # Sketch mode takes highest priority, other modes are irrelevant.
- dxfCreatePart = False
- dxfCreateDraft = False
- dxfMakeBlocks = False
- else:
- # Not in sketch mode, so determine structure from DxfImportMode.
- # This is where the new parameter is read, with its default value defined.
- # 0=Draft, 1=Primitives, 2=Shapes, 3=Fused
- import_mode = params.get_param("DxfImportMode", 2) # Default to "Individual part shapes"
-
- if import_mode == 3: # Fused part shapes
- dxfMakeBlocks = True # 'groupLayers' is the legacy equivalent
- dxfCreatePart = False # In legacy, dxfMakeBlocks overrides these
- dxfCreateDraft = False
- elif import_mode == 0: # Editable draft objects
- dxfMakeBlocks = False
+ if dxfCreateSketch:
dxfCreatePart = False
- dxfCreateDraft = True
- else: # Covers modes 1 (Primitives) and 2 (Shapes). Legacy maps both to "Simple part shapes"
- dxfMakeBlocks = False
- dxfCreatePart = True
dxfCreateDraft = False
+ dxfMakeBlocks = False
+ else:
+ # Read the new unified mode parameter and translate it to the old flags
+ # 0=Draft, 1=Primitives, 2=Shapes, 3=Fused
+ import_mode = hGrp.GetInt("DxfImportMode", 2) # Default to "Individual shapes"
+ if import_mode == 3: # Fused part shapes
+ dxfMakeBlocks = True
+ dxfCreatePart = False
+ dxfCreateDraft = False
+ elif import_mode == 0: # Editable draft objects
+ dxfMakeBlocks = False
+ dxfCreatePart = False
+ dxfCreateDraft = True
+ else: # Individual part shapes or Primitives
+ dxfMakeBlocks = False
+ dxfCreatePart = True
+ dxfCreateDraft = False
+
+ # The legacy importer still uses these global variables, so we read them all.
+ dxfDiscretizeCurves = hGrp.GetBool("DiscretizeEllipses", True)
+ dxfStarBlocks = hGrp.GetBool("dxfstarblocks", False)
+ dxfJoin = hGrp.GetBool("joingeometry", False)
+ dxfRenderPolylineWidth = hGrp.GetBool("renderPolylineWidth", False)
+ dxfImportTexts = hGrp.GetBool("dxftext", False)
+ dxfImportLayouts = hGrp.GetBool("dxflayout", False)
+ dxfImportPoints = hGrp.GetBool("dxfImportPoints", True)
+ dxfImportHatches = hGrp.GetBool("importDxfHatches", False)
+ dxfUseStandardSize = hGrp.GetBool("dxfStdSize", False)
+ dxfGetColors = hGrp.GetBool("dxfGetOriginalColors", True)
+ dxfUseDraftVisGroups = hGrp.GetBool("dxfUseDraftVisGroups", True)
+ dxfMakeFaceMode = hGrp.GetBool("MakeFaceMode", False)
+ dxfUseLegacyExporter = hGrp.GetBool("dxfUseLegacyExporter", False)
+ dxfExportBlocks = hGrp.GetBool("dxfExportBlocks", True)
+ dxfScaling = hGrp.GetFloat("dxfScaling", 1.0)
- # --- Other settings that are not checkboxes ---
dxfBrightBackground = isBrightBackground()
dxfDefaultColor = getColor()
-
class DxfImportReporter:
"""Formats and reports statistics from a DXF import process."""
def __init__(self, filename, stats_dict, total_time=0.0):
diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.cpp b/src/Mod/Import/App/dxf/ImpExpDxf.cpp
index 3319fe27a2..3ad107dc58 100644
--- a/src/Mod/Import/App/dxf/ImpExpDxf.cpp
+++ b/src/Mod/Import/App/dxf/ImpExpDxf.cpp
@@ -53,6 +53,7 @@
#include
#endif
+#include
#include
#include
#include
@@ -80,6 +81,35 @@ using namespace Import;
using BRepAdaptor_HCurve = BRepAdaptor_Curve;
#endif
+std::map ImpExpDxfRead::PreScan(const std::string& filepath)
+{
+ std::map counts;
+ std::ifstream ifs(filepath);
+ if (!ifs) {
+ // Could throw an exception or log an error
+ return counts;
+ }
+
+ std::string line;
+ bool next_is_entity_name = false;
+
+ while (std::getline(ifs, line)) {
+ // Simple trim for Windows-style carriage returns
+ if (!line.empty() && line.back() == '\r') {
+ line.pop_back();
+ }
+
+ if (next_is_entity_name) {
+ // The line after a " 0" group code is the entity type
+ counts[line]++;
+ next_is_entity_name = false;
+ }
+ else if (line == " 0") {
+ next_is_entity_name = true;
+ }
+ }
+ return counts;
+}
//******************************************************************************
// reading
diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.h b/src/Mod/Import/App/dxf/ImpExpDxf.h
index 5a1855a651..bdf38a8623 100644
--- a/src/Mod/Import/App/dxf/ImpExpDxf.h
+++ b/src/Mod/Import/App/dxf/ImpExpDxf.h
@@ -61,7 +61,7 @@ public:
{
Py_XDECREF(DraftModule);
}
-
+ static std::map PreScan(const std::string& filepath);
void StartImport() override;
Py::Object getStatsAsPyObject();
diff --git a/src/Mod/Import/Gui/AppImportGuiPy.cpp b/src/Mod/Import/Gui/AppImportGuiPy.cpp
index 0e4dc03c11..7654f1d4e7 100644
--- a/src/Mod/Import/Gui/AppImportGuiPy.cpp
+++ b/src/Mod/Import/Gui/AppImportGuiPy.cpp
@@ -94,6 +94,7 @@ public:
add_keyword_method("insert",
&Module::insert,
"insert(string,string) -- Insert the file into the given document.");
+ add_varargs_method("preScanDxf", &Module::preScanDxf, "preScanDxf(filepath) -> dict");
add_varargs_method("readDXF",
&Module::readDXF,
"readDXF(filename,[document,ignore_errors,option_source]): Imports a "
@@ -112,6 +113,26 @@ public:
}
private:
+ Py::Object preScanDxf(const Py::Tuple& args)
+ {
+ char* filepath_char = nullptr;
+ if (!PyArg_ParseTuple(args.ptr(), "et", "utf-8", &filepath_char)) {
+ throw Py::Exception();
+ }
+ std::string filepath(filepath_char);
+ PyMem_Free(filepath_char);
+
+#include
+
+ std::map counts = Import::ImpExpDxfRead::PreScan(filepath);
+
+ Py::Dict result;
+ for (const auto& pair : counts) {
+ result.setItem(Py::String(pair.first), Py::Long(pair.second));
+ }
+ return result;
+ }
+
Py::Object importOptions(const Py::Tuple& args)
{
char* Name {};