Merge pull request #21985 from furgo16/dxf-stats-reporter

Import: add DXF statistics reporter
This commit is contained in:
Chris Hennes
2025-06-30 10:55:57 -05:00
committed by GitHub
7 changed files with 349 additions and 42 deletions

View File

@@ -54,6 +54,7 @@ import sys
import os
import math
import re
import time
import FreeCAD
import Part
import Draft
@@ -2810,6 +2811,8 @@ def open(filename):
Use local variables, not global variables.
"""
readPreferences()
total_start_time = time.perf_counter()
if dxfUseLegacyImporter:
getDXFlibs()
if dxfReader:
@@ -2825,12 +2828,19 @@ def open(filename):
doc = FreeCAD.newDocument(docname)
doc.Label = docname
FreeCAD.setActiveDocument(doc.Name)
stats = None
if gui:
import ImportGui
ImportGui.readDXF(filename)
stats = ImportGui.readDXF(filename)
else:
import Import
Import.readDXF(filename)
stats = Import.readDXF(filename)
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()
@@ -2855,6 +2865,7 @@ def insert(filename, docname):
Use local variables, not global variables.
"""
readPreferences()
total_start_time = time.perf_counter()
try:
doc = FreeCAD.getDocument(docname)
except NameError:
@@ -2867,12 +2878,19 @@ def insert(filename, docname):
else:
errorDXFLib(gui)
else:
stats = None
if gui:
import ImportGui
ImportGui.readDXF(filename)
stats = ImportGui.readDXF(filename)
else:
import Import
Import.readDXF(filename)
stats = Import.readDXF(filename)
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()
@@ -4197,3 +4215,122 @@ def readPreferences():
dxfDefaultColor = getColor()
dxfExportBlocks = params.get_param("dxfExportBlocks")
dxfScaling = params.get_param("dxfScaling")
class DxfImportReporter:
"""Formats and reports statistics from a DXF import process."""
def __init__(self, filename, stats_dict, total_time=0.0):
self.filename = filename
self.stats = stats_dict
self.total_time = total_time
def to_console_string(self):
"""
Formats the statistics into a human-readable string for console output.
"""
if not self.stats:
return "DXF Import: no statistics were returned from the importer.\n"
lines = ["\n--- DXF import summary ---"]
lines.append(f"Import of file: '{self.filename}'\n")
# General info
lines.append(f"DXF version: {self.stats.get('dxfVersion', 'Unknown')}")
lines.append(f"File encoding: {self.stats.get('dxfEncoding', 'Unknown')}")
# Scaling info
file_units = self.stats.get('fileUnits', 'Not specified')
source = self.stats.get('scalingSource', '')
if source:
lines.append(f"File units: {file_units} (from {source})")
else:
lines.append(f"File units: {file_units}")
manual_scaling = self.stats.get('importSettings', {}).get('Manual scaling factor', '1.0')
lines.append(f"Manual scaling factor: {manual_scaling}")
final_scaling = self.stats.get('finalScalingFactor', 1.0)
lines.append(f"Final scaling: 1 DXF unit = {final_scaling:.4f} mm")
lines.append("")
# Timing
lines.append("Performance:")
cpp_time = self.stats.get('importTimeSeconds', 0.0)
lines.append(f" - C++ import time: {cpp_time:.4f} seconds")
lines.append(f" - Total import time: {self.total_time:.4f} seconds")
lines.append("")
# Settings
lines.append("Import settings:")
settings = self.stats.get('importSettings', {})
if settings:
for key, value in sorted(settings.items()):
lines.append(f" - {key}: {value}")
else:
lines.append(" (No settings recorded)")
lines.append("")
# Counts
lines.append("Entity counts:")
total_read = 0
unsupported_keys = self.stats.get('unsupportedFeatures', {}).keys()
unsupported_entity_names = set()
for key in unsupported_keys:
# Extract the entity name from the key string, e.g., 'HATCH' from "Entity type 'HATCH'"
entity_name_match = re.search(r"\'(.*?)\'", key)
if entity_name_match:
unsupported_entity_names.add(entity_name_match.group(1))
has_unsupported_indicator = False
entities = self.stats.get('entityCounts', {})
if entities:
for key, value in sorted(entities.items()):
indicator = ""
if key in unsupported_entity_names:
indicator = " (*)"
has_unsupported_indicator = True
lines.append(f" - {key}: {value}{indicator}")
total_read += value
lines.append("----------------------------")
lines.append(f" Total entities read: {total_read}")
else:
lines.append(" (No entities recorded)")
lines.append(f"FreeCAD objects created: {self.stats.get('totalEntitiesCreated', 0)}")
lines.append("")
if has_unsupported_indicator:
lines.append("(*) Entity type not supported by importer.")
lines.append("")
lines.append("Unsupported features:")
unsupported = self.stats.get('unsupportedFeatures', {})
if unsupported:
for key, occurrences in sorted(unsupported.items()):
count = len(occurrences)
max_details_to_show = 5
details_list = []
for i, (line, handle) in enumerate(occurrences):
if i >= max_details_to_show:
break
if handle:
details_list.append(f"line {line} (handle {handle})")
else:
details_list.append(f"line {line} (no handle available)")
details_str = ", ".join(details_list)
if count > max_details_to_show:
lines.append(f" - {key}: {count} time(s). Examples: {details_str}, ...")
else:
lines.append(f" - {key}: {count} time(s) at {details_str}")
else:
lines.append(" (none)")
lines.append("--- End of summary ---\n")
return "\n".join(lines)
def report_to_console(self):
"""
Prints the formatted statistics string to the FreeCAD console.
"""
output_string = self.to_console_string()
FCC.PrintMessage(output_string)

View File

@@ -48,6 +48,7 @@
#endif
#endif
#include <chrono>
#include "dxf/ImpExpDxf.h"
#include "SketchExportHelper.h"
#include <App/Application.h>
@@ -413,8 +414,15 @@ private:
ImpExpDxfRead dxf_file(EncodedName, pcDoc);
dxf_file.setOptionSource(defaultOptions);
dxf_file.setOptions();
auto startTime = std::chrono::high_resolution_clock::now();
dxf_file.DoRead(IgnoreErrors);
auto endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = endTime - startTime;
dxf_file.setImportTime(elapsed.count());
pcDoc->recompute();
return dxf_file.getStatsAsPyObject();
}
catch (const Standard_Failure& e) {
throw Py::RuntimeError(e.GetMessageString());
@@ -422,7 +430,6 @@ private:
catch (const Base::Exception& e) {
throw Py::RuntimeError(e.what());
}
return Py::None();
}

View File

@@ -141,22 +141,32 @@ void ImpExpDxfRead::setOptions()
{
ParameterGrp::handle hGrp =
App::GetApplication().GetParameterGroupByPath(getOptionSource().c_str());
m_stats.importSettings.clear();
m_preserveLayers = hGrp->GetBool("dxfUseDraftVisGroups", true);
m_stats.importSettings["Use layers"] = m_preserveLayers ? "Yes" : "No";
m_preserveColors = hGrp->GetBool("dxfGetOriginalColors", true);
m_stats.importSettings["Use colors from the DXF file"] = m_preserveColors ? "Yes" : "No";
// Default for creation type is to create draft objects.
// The radio-button structure of the options dialog should generally prevent this condition.
m_mergeOption = DraftObjects;
m_stats.importSettings["Merge option"] = "Create Draft objects"; // Default
if (hGrp->GetBool("groupLayers", true)) {
// Group all compatible objects together
m_mergeOption = MergeShapes;
m_stats.importSettings["Merge option"] = "Group layers into blocks";
}
else if (hGrp->GetBool("dxfCreatePart", true)) {
// Create (non-draft) Shape objects when possible
m_mergeOption = SingleShapes;
m_stats.importSettings["Merge option"] = "Create Part shapes";
}
else if (hGrp->GetBool("dxfCreateDraft", true)) {
// Create only Draft objects, making the result closest to drawn-from-scratch
m_mergeOption = DraftObjects;
m_stats.importSettings["Merge option"] = "Create Draft objects";
}
// TODO: joingeometry should give an intermediate between MergeShapes and SingleShapes which
// will merge shapes that happen to join end-to-end. As such it should be in the radio button
@@ -164,12 +174,25 @@ void ImpExpDxfRead::setOptions()
// this really means is there should be an "Import as sketch" checkbox, and only the
// MergeShapes, JoinShapes, and SingleShapes radio buttons should be allowed, i.e. Draft Objects
// would be ignored.
SetAdditionalScaling(hGrp->GetFloat("dxfScaling", 1.0));
bool joinGeometry = hGrp->GetBool("joingeometry", false);
m_stats.importSettings["Join geometry"] = joinGeometry ? "Yes" : "No";
double scaling = hGrp->GetFloat("dxfScaling", 1.0);
SetAdditionalScaling(scaling);
m_stats.importSettings["Manual scaling factor"] = std::to_string(scaling);
m_importAnnotations = hGrp->GetBool("dxftext", false);
m_stats.importSettings["Import texts and dimensions"] = m_importAnnotations ? "Yes" : "No";
m_importPoints = hGrp->GetBool("dxfImportPoints", true);
m_stats.importSettings["Import points"] = m_importPoints ? "Yes" : "No";
m_importPaperSpaceEntities = hGrp->GetBool("dxflayout", false);
m_stats.importSettings["Import layout objects"] = m_importPaperSpaceEntities ? "Yes" : "No";
m_importHiddenBlocks = hGrp->GetBool("dxfstarblocks", false);
m_stats.importSettings["Import hidden blocks"] = m_importHiddenBlocks ? "Yes" : "No";
// TODO: There is currently no option for this: m_importFrozenLayers =
// hGrp->GetBool("dxffrozenLayers", false);
// TODO: There is currently no option for this: m_importHiddenLayers =
@@ -771,6 +794,7 @@ std::string ImpExpDxfRead::Deformat(const char* text)
void ImpExpDxfRead::DrawingEntityCollector::AddObject(const TopoDS_Shape& shape,
const char* nameBase)
{
Reader.IncrementCreatedObjectCount();
auto pcFeature = Reader.document->addObject<Part::Feature>(nameBase);
pcFeature->Shape.setValue(shape);
Reader.MoveToLayer(pcFeature);
@@ -778,6 +802,7 @@ void ImpExpDxfRead::DrawingEntityCollector::AddObject(const TopoDS_Shape& shape,
}
void ImpExpDxfRead::DrawingEntityCollector::AddObject(FeaturePythonBuilder shapeBuilder)
{
Reader.IncrementCreatedObjectCount();
App::FeaturePython* shape = shapeBuilder(Reader.OCSOrientationTransform);
if (shape != nullptr) {
Reader.MoveToLayer(shape);
@@ -1347,3 +1372,49 @@ void ImpExpDxfWrite::exportDiametricDim(Base::Vector3d textLocn,
arc2[2] = arcPoint2.z;
writeDiametricDim(text, arc1, arc2, dimText);
}
Py::Object ImpExpDxfRead::getStatsAsPyObject()
{
// Create a Python dictionary to hold all import statistics.
Py::Dict statsDict;
// Populate the dictionary with general information about the import.
statsDict.setItem("dxfVersion", Py::String(m_stats.dxfVersion));
statsDict.setItem("dxfEncoding", Py::String(m_stats.dxfEncoding));
statsDict.setItem("scalingSource", Py::String(m_stats.scalingSource));
statsDict.setItem("fileUnits", Py::String(m_stats.fileUnits));
statsDict.setItem("finalScalingFactor", Py::Float(m_stats.finalScalingFactor));
statsDict.setItem("importTimeSeconds", Py::Float(m_stats.importTimeSeconds));
statsDict.setItem("totalEntitiesCreated", Py::Long(m_stats.totalEntitiesCreated));
// Create a nested dictionary for the counts of each DXF entity type read.
Py::Dict entityCountsDict;
for (const auto& pair : m_stats.entityCounts) {
entityCountsDict.setItem(pair.first.c_str(), Py::Long(pair.second));
}
statsDict.setItem("entityCounts", entityCountsDict);
// Create a nested dictionary for the import settings used for this session.
Py::Dict importSettingsDict;
for (const auto& pair : m_stats.importSettings) {
importSettingsDict.setItem(pair.first.c_str(), Py::String(pair.second));
}
statsDict.setItem("importSettings", importSettingsDict);
// Create a nested dictionary for any unsupported DXF features encountered.
Py::Dict unsupportedFeaturesDict;
for (const auto& pair : m_stats.unsupportedFeatures) {
Py::List occurrencesList;
for (const auto& occurrence : pair.second) {
Py::Tuple infoTuple(2);
infoTuple.setItem(0, Py::Long(occurrence.first));
infoTuple.setItem(1, Py::String(occurrence.second));
occurrencesList.append(infoTuple);
}
unsupportedFeaturesDict.setItem(pair.first.c_str(), occurrencesList);
}
statsDict.setItem("unsupportedFeatures", unsupportedFeaturesDict);
// Return the fully populated statistics dictionary to the Python caller.
return statsDict;
}

View File

@@ -50,6 +50,8 @@ public:
Py_XDECREF(DraftModule);
}
Py::Object getStatsAsPyObject();
bool ReadEntitiesSection() override;
// CDxfRead's virtual functions
@@ -204,6 +206,11 @@ private:
std::string m_optionSource;
protected:
friend class DrawingEntityCollector;
void IncrementCreatedObjectCount()
{
m_stats.totalEntitiesCreated++;
}
virtual void ApplyGuiStyles(Part::Feature* /*object*/) const
{}
virtual void ApplyGuiStyles(App::FeaturePython* /*object*/) const

View File

@@ -26,6 +26,61 @@
using namespace std;
namespace
{
std::string DxfUnitToString(DxfUnits::eDxfUnits_t unit)
{
switch (unit) {
case DxfUnits::eInches:
return "Inches";
case DxfUnits::eFeet:
return "Feet";
case DxfUnits::eMiles:
return "Miles";
case DxfUnits::eMillimeters:
return "Millimeters";
case DxfUnits::eCentimeters:
return "Centimeters";
case DxfUnits::eMeters:
return "Meters";
case DxfUnits::eKilometers:
return "Kilometers";
case DxfUnits::eMicroinches:
return "Microinches";
case DxfUnits::eMils:
return "Mils";
case DxfUnits::eYards:
return "Yards";
case DxfUnits::eAngstroms:
return "Angstroms";
case DxfUnits::eNanometers:
return "Nanometers";
case DxfUnits::eMicrons:
return "Microns";
case DxfUnits::eDecimeters:
return "Decimeters";
case DxfUnits::eDekameters:
return "Dekameters";
case DxfUnits::eHectometers:
return "Hectometers";
case DxfUnits::eGigameters:
return "Gigameters";
case DxfUnits::eAstronomicalUnits:
return "Astronomical Units";
case DxfUnits::eLightYears:
return "Light Years";
case DxfUnits::eParsecs:
return "Parsecs";
case DxfUnits::eUnspecified:
default:
return "Unspecified";
}
}
} // namespace
static Base::Vector3d MakeVector3d(const double coordinates[3])
{
// NOLINTNEXTLINE(readability/nolint)
@@ -1947,6 +2002,9 @@ void CDxfRead::ProcessAllEntityAttributes()
void CDxfRead::ResolveEntityAttributes()
{
m_entityAttributes.ResolveBylayerAttributes(*this);
if (m_entityAttributes.m_paperSpace) {
m_stats.entityCounts["ENTITIES_IN_PAPERSPACE"]++;
}
// TODO: Look at the space and layer (hidden/frozen?) and options and return false if the entity
// is not needed.
// TODO: INSERT must not call this because an INSERT on a hidden layer should always be
@@ -2284,8 +2342,8 @@ bool CDxfRead::ReadDimension()
bool CDxfRead::ReadUnknownEntity()
{
UnsupportedFeature("Entity type '%s'", m_record_data);
ProcessAllEntityAttributes();
UnsupportedFeature("Entity type '%s'", m_current_entity_name.c_str());
return true;
}
@@ -2294,6 +2352,7 @@ bool CDxfRead::ReadBlockInfo()
int blockType = 0;
std::string blockName;
InitializeAttributes();
m_stats.entityCounts["BLOCK"]++;
// Both 2 and 3 are the block name.
SetupStringAttribute(eName, blockName);
SetupStringAttribute(eExtraText, blockName);
@@ -2344,11 +2403,8 @@ void CDxfRead::UnsupportedFeature(const char* format, args&&... argValuess)
{
// NOLINTNEXTLINE(runtime/printf)
std::string formattedMessage = fmt::sprintf(format, std::forward<args>(argValuess)...);
// We place these formatted messages in a map, count their occurrences and not their first
// occurrence.
if (m_unsupportedFeaturesNoted[formattedMessage].first++ == 0) {
m_unsupportedFeaturesNoted[formattedMessage].second = m_line;
}
m_stats.unsupportedFeatures[formattedMessage].emplace_back(m_current_entity_line_number,
m_current_entity_handle);
}
bool CDxfRead::get_next_record()
@@ -2538,6 +2594,8 @@ bool CDxfRead::ReadVersion()
m_version = RUnknown;
}
m_stats.dxfVersion = m_record_data;
return ResolveEncoding();
}
@@ -2606,6 +2664,9 @@ bool CDxfRead::ResolveEncoding()
Py_DECREF(pyDecoder);
Py_DECREF(pyUTF8Decoder);
}
m_stats.dxfEncoding = m_encoding;
return !m_encoding.empty();
}
@@ -2664,17 +2725,6 @@ void CDxfRead::DoRead(const bool ignore_errors /* = false */)
}
}
FinishImport();
// Flush out any unsupported features messages
if (!m_unsupportedFeaturesNoted.empty()) {
ImportError("Unsupported DXF features:\n");
for (auto& featureInfo : m_unsupportedFeaturesNoted) {
ImportError("%s: %d time(s) first at line %d\n",
featureInfo.first,
featureInfo.second.first,
featureInfo.second.second);
}
}
}
catch (const Base::Exception& e) {
// This catches specific FreeCAD exceptions and re-throws them.
@@ -2747,8 +2797,12 @@ void CDxfRead::ProcessLayerReference(CDxfRead* object, void* target)
}
bool CDxfRead::ReadEntity()
{
m_current_entity_line_number = m_line;
m_current_entity_name = m_record_data;
InitializeAttributes();
m_entityAttributes.SetDefaults();
m_current_entity_handle.clear();
SetupStringAttribute(eHandle, m_current_entity_handle);
EntityNormalVector.Set(0, 0, 1);
Setup3DVectorAttribute(eExtrusionDirection, EntityNormalVector);
SetupStringAttribute(eLinetypeName, m_entityAttributes.m_LineType);
@@ -2759,6 +2813,9 @@ bool CDxfRead::ReadEntity()
m_entityAttributes.m_paperSpace); // TODO: Ensure the stream is noboolalpha (for that
// matter ensure the stream has the "C" locale
SetupValueAttribute(eColor, m_entityAttributes.m_Color);
m_stats.entityCounts[m_record_data]++;
// The entity record is already the current record and is already checked as a type 0 record
if (IsObjectName("LINE")) {
return ReadLine();
@@ -2809,13 +2866,11 @@ bool CDxfRead::ReadHeaderSection()
if (m_record_type == eObjectType && IsObjectName("ENDSEC")) {
if (m_unitScalingFactor == 0.0) {
// Neither INSUNITS nor MEASUREMENT found, assume 1 DXF unit = 1mm
// TODO: Perhaps this default should depend on the current measuring units of the
// app.
// TODO: Perhaps this default should depend on the current project's unit system
m_unitScalingFactor = m_additionalScaling;
ImportObservation("No INSUNITS or MEASUREMENT; setting scaling to 1 DXF unit = "
"%gmm based on DXF scaling option\n",
m_unitScalingFactor);
m_stats.fileUnits = "Unspecified (Defaulting to 1:1)";
}
m_stats.finalScalingFactor = m_unitScalingFactor;
return true;
}
if (m_record_type != eVariableName) {
@@ -2843,14 +2898,14 @@ bool CDxfRead::ReadVariable()
if (!ParseValue<int>(this, &varValue)) {
ImportError("Failed to get integer from INSUNITS value '%s'\n", m_record_data);
}
else if (auto units = DxfUnits::eDxfUnits_t(varValue); !DxfUnits::IsValid(units)) {
ImportError("Unknown value '%d' for INSUNITS\n", varValue);
}
else {
auto units = DxfUnits::eDxfUnits_t(varValue);
if (!DxfUnits::IsValid(units)) {
units = DxfUnits::eUnspecified;
}
m_unitScalingFactor = DxfUnits::Factor(units) * m_additionalScaling;
ImportObservation("Setting scaling to 1 DXF unit = %gmm based on INSUNITS and "
"DXF scaling option\n",
m_unitScalingFactor);
m_stats.scalingSource = "$INSUNITS";
m_stats.fileUnits = DxfUnitToString(units);
}
return true;
}
@@ -2858,12 +2913,10 @@ bool CDxfRead::ReadVariable()
get_next_record();
int varValue = 1;
if (m_unitScalingFactor == 0.0 && ParseValue<int>(this, &varValue)) {
m_unitScalingFactor =
DxfUnits::Factor(varValue != 0 ? DxfUnits::eMillimeters : DxfUnits::eInches)
* m_additionalScaling;
ImportObservation("Setting scaling to 1 DXF unit = %gmm based on MEASUREMENT and "
"DXF scaling option\n",
m_unitScalingFactor);
auto units = (varValue != 0 ? DxfUnits::eMillimeters : DxfUnits::eInches);
m_unitScalingFactor = DxfUnits::Factor(units) * m_additionalScaling;
m_stats.scalingSource = "$MEASUREMENT";
m_stats.fileUnits = DxfUnitToString(units);
}
return true;
}
@@ -3131,3 +3184,5 @@ Base::Color CDxfRead::ObjectColor(ColorIndex_t index)
return result;
}
// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
template void CDxfRead::UnsupportedFeature<>(const char*);

View File

@@ -176,6 +176,21 @@ struct LWPolyDataOut
point3D Extr;
};
// Statistics reporting structure
struct DxfImportStats
{
double importTimeSeconds = 0.0;
std::string dxfVersion;
std::string dxfEncoding;
std::string scalingSource;
std::string fileUnits;
double finalScalingFactor = 1.0;
std::map<std::string, int> entityCounts;
std::map<std::string, std::string> importSettings;
std::map<std::string, std::vector<std::pair<int, std::string>>> unsupportedFeatures;
int totalEntitiesCreated = 0;
};
// "using" for enums is not supported by all platforms
// https://stackoverflow.com/questions/41167119/how-to-fix-a-wsubobject-linkage-warning
@@ -185,6 +200,7 @@ enum eDXFGroupCode_t
ePrimaryText = 1,
eName = 2,
eExtraText = 3,
eHandle = 5,
eLinetypeName = 6,
eTextStyleName = 7,
eLayerName = 8,
@@ -447,6 +463,9 @@ private:
bool m_not_eof = true;
int m_line = 0;
bool m_repeat_last_record = false;
int m_current_entity_line_number = 0;
std::string m_current_entity_name;
std::string m_current_entity_handle;
// The scaling from DXF units to millimetres.
// This does not include the dxfScaling option
@@ -455,6 +474,7 @@ private:
double m_unitScalingFactor = 0.0;
protected:
DxfImportStats m_stats;
// An additional scaling factor which can be modified before readDXF is called, and will be
// incorporated into m_unitScalingFactor.
void SetAdditionalScaling(double scaling)
@@ -700,7 +720,6 @@ protected:
void UnsupportedFeature(const char* format, args&&... argValues);
private:
std::map<std::string, std::pair<int, int>> m_unsupportedFeaturesNoted;
std::string m_CodePage; // Code Page name from $DWGCODEPAGE or null if none/not read yet
// The following was going to be python's canonical name for the encoding, but this is (a) not
// easily found and (b) does not speed up finding the encoding object.
@@ -846,6 +865,10 @@ public:
{
return m_fail;
}
void setImportTime(double seconds)
{
m_stats.importTimeSeconds = seconds;
}
void
DoRead(bool ignore_errors = false); // this reads the file and calls the following functions
virtual void StartImport()

View File

@@ -46,6 +46,7 @@
#endif
#endif
#include <chrono>
#include "ExportOCAFGui.h"
#include "ImportOCAFGui.h"
#include "OCAFBrowser.h"
@@ -401,8 +402,15 @@ private:
ImpExpDxfReadGui dxf_file(EncodedName, pcDoc);
dxf_file.setOptionSource(defaultOptions);
dxf_file.setOptions();
auto startTime = std::chrono::high_resolution_clock::now();
dxf_file.DoRead(IgnoreErrors);
auto endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = endTime - startTime;
dxf_file.setImportTime(elapsed.count());
pcDoc->recompute();
return dxf_file.getStatsAsPyObject();
}
catch (const Standard_Failure& e) {
throw Py::RuntimeError(e.GetMessageString());
@@ -410,7 +418,6 @@ private:
catch (const Base::Exception& e) {
throw Py::RuntimeError(e.what());
}
return Py::None();
}
Py::Object exportOptions(const Py::Tuple& args)