mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-15 19:14:12 +02:00
Merge pull request #750 from ispyisail/feature/dxf-paint-device
Export the master-side cross-reference table to DXF
This commit is contained in:
@@ -177,6 +177,8 @@ set(QET_SRC_FILES
|
|||||||
${QET_DIR}/sources/configdialog.h
|
${QET_DIR}/sources/configdialog.h
|
||||||
${QET_DIR}/sources/createdxf.cpp
|
${QET_DIR}/sources/createdxf.cpp
|
||||||
${QET_DIR}/sources/createdxf.h
|
${QET_DIR}/sources/createdxf.h
|
||||||
|
${QET_DIR}/sources/dxfpaintdevice.cpp
|
||||||
|
${QET_DIR}/sources/dxfpaintdevice.h
|
||||||
${QET_DIR}/sources/diagramcommands.cpp
|
${QET_DIR}/sources/diagramcommands.cpp
|
||||||
${QET_DIR}/sources/diagramcommands.h
|
${QET_DIR}/sources/diagramcommands.h
|
||||||
${QET_DIR}/sources/diagramcontent.cpp
|
${QET_DIR}/sources/diagramcontent.cpp
|
||||||
|
|||||||
@@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2006-2026 The QElectroTech Team
|
||||||
|
This file is part of QElectroTech.
|
||||||
|
|
||||||
|
QElectroTech is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
QElectroTech is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "dxfpaintdevice.h"
|
||||||
|
#include "createdxf.h"
|
||||||
|
|
||||||
|
#include <QPainterPath>
|
||||||
|
#include <QTextItem>
|
||||||
|
#include <QtMath>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief DxfPaintEngine::DxfPaintEngine
|
||||||
|
@param filepath path of the already-open DXF file entities are
|
||||||
|
appended to (see Createdxf::dxfBegin()/dxfEnd()).
|
||||||
|
*/
|
||||||
|
DxfPaintEngine::DxfPaintEngine(const QString &filepath) :
|
||||||
|
QPaintEngine(QPaintEngine::AllFeatures),
|
||||||
|
m_filepath(filepath)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxfPaintEngine::begin(QPaintDevice *pdev)
|
||||||
|
{
|
||||||
|
Q_UNUSED(pdev)
|
||||||
|
setActive(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxfPaintEngine::end()
|
||||||
|
{
|
||||||
|
setActive(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief DxfPaintEngine::updateState
|
||||||
|
Track the properties this engine cares about. Everything drawn is
|
||||||
|
mapped through the transform in effect at draw time - this is what
|
||||||
|
lets an item's paint() be replayed unmodified at whatever scene
|
||||||
|
position/rotation it actually has, exactly as QPainter does for
|
||||||
|
on-screen painting.
|
||||||
|
*/
|
||||||
|
void DxfPaintEngine::updateState(const QPaintEngineState &state)
|
||||||
|
{
|
||||||
|
QPaintEngine::DirtyFlags flags = state.state();
|
||||||
|
|
||||||
|
if (flags & QPaintEngine::DirtyTransform)
|
||||||
|
m_world_transform = state.transform();
|
||||||
|
if (flags & QPaintEngine::DirtyPen)
|
||||||
|
m_pen = state.pen();
|
||||||
|
if (flags & QPaintEngine::DirtyBrush)
|
||||||
|
m_brush = state.brush();
|
||||||
|
if (flags & QPaintEngine::DirtyFont)
|
||||||
|
m_font = state.font();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF DxfPaintEngine::toDxf(const QPointF &local_point) const
|
||||||
|
{
|
||||||
|
// Createdxf's QRectF/QLineF/QPolygonF/QPointF convenience overloads
|
||||||
|
// each apply xScale/yScale and the sheetHeight Y-flip internally, so
|
||||||
|
// this engine's whole job is composing the world transform - not
|
||||||
|
// touching DXF units at all.
|
||||||
|
return m_world_transform.map(local_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxfPaintEngine::strokePolygon(const QPolygonF &poly, bool close)
|
||||||
|
{
|
||||||
|
if (poly.size() < 2)
|
||||||
|
return;
|
||||||
|
if (m_pen.style() == Qt::NoPen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QPolygonF mapped;
|
||||||
|
mapped.reserve(poly.size());
|
||||||
|
for (const QPointF &p : poly)
|
||||||
|
mapped << toDxf(p);
|
||||||
|
|
||||||
|
if (close && mapped.first() != mapped.last())
|
||||||
|
mapped << mapped.first();
|
||||||
|
|
||||||
|
Createdxf::drawPolyline(m_filepath, mapped, Createdxf::dxfColor(m_pen));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxfPaintEngine::drawLines(const QLineF *lines, int lineCount)
|
||||||
|
{
|
||||||
|
if (m_pen.style() == Qt::NoPen)
|
||||||
|
return;
|
||||||
|
for (int i = 0; i < lineCount; ++i)
|
||||||
|
{
|
||||||
|
QLineF mapped(toDxf(lines[i].p1()), toDxf(lines[i].p2()));
|
||||||
|
Createdxf::drawLine(m_filepath, mapped, Createdxf::dxfColor(m_pen));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxfPaintEngine::drawRects(const QRectF *rects, int rectCount)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < rectCount; ++i)
|
||||||
|
{
|
||||||
|
QPolygonF corners;
|
||||||
|
corners << rects[i].topLeft() << rects[i].topRight()
|
||||||
|
<< rects[i].bottomRight() << rects[i].bottomLeft();
|
||||||
|
|
||||||
|
// No HATCH support in v1 (see the design note in the header) - a
|
||||||
|
// filled rect is emitted as its outline only, same as an unfilled
|
||||||
|
// one. Still emitted (not skipped) when there's no pen but there
|
||||||
|
// is a brush, so a filled-only shape isn't dropped entirely.
|
||||||
|
if (m_pen.style() != Qt::NoPen || m_brush.style() != Qt::NoBrush)
|
||||||
|
strokePolygon(corners, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxfPaintEngine::drawEllipse(const QRectF &rect)
|
||||||
|
{
|
||||||
|
if (m_pen.style() == Qt::NoPen && m_brush.style() == Qt::NoBrush)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// A non axis-aligned ellipse (rotation baked into m_world_transform)
|
||||||
|
// can't be expressed as Createdxf::drawEllipse(), which only accepts
|
||||||
|
// an axis-aligned QRectF. Approximate with a flattened polygon in
|
||||||
|
// that case; use the exact CIRCLE/ELLIPSE entity when the transform
|
||||||
|
// is axis-aligned, matching what the existing hand-written exporters
|
||||||
|
// already produce for unrotated shapes.
|
||||||
|
const bool axis_aligned = qFuzzyIsNull(m_world_transform.m12())
|
||||||
|
&& qFuzzyIsNull(m_world_transform.m21());
|
||||||
|
if (axis_aligned)
|
||||||
|
{
|
||||||
|
QRectF mapped = m_world_transform.mapRect(rect);
|
||||||
|
Createdxf::drawEllipse(m_filepath, mapped, Createdxf::dxfColor(m_pen));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath path;
|
||||||
|
path.addEllipse(rect);
|
||||||
|
const auto polygons = path.toSubpathPolygons();
|
||||||
|
for (const QPolygonF &poly : polygons)
|
||||||
|
strokePolygon(poly, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief DxfPaintEngine::drawPath
|
||||||
|
Handles drawArc()/drawPie() (QPainterPath::Arc elements - emitted as
|
||||||
|
chord segments, since Createdxf's ARC-equivalent
|
||||||
|
(drawArcEllipse()) only takes an axis-aligned ellipse + angle pair,
|
||||||
|
which doesn't compose with an arbitrary path transform) and
|
||||||
|
fillPath()/generic paths (flattened to polylines).
|
||||||
|
*/
|
||||||
|
void DxfPaintEngine::drawPath(const QPainterPath &path)
|
||||||
|
{
|
||||||
|
if (m_pen.style() == Qt::NoPen && m_brush.style() == Qt::NoBrush)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto polygons = path.toSubpathPolygons();
|
||||||
|
for (const QPolygonF &poly : polygons)
|
||||||
|
strokePolygon(poly, path.isEmpty() ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxfPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
|
||||||
|
{
|
||||||
|
if (pointCount < 2)
|
||||||
|
return;
|
||||||
|
QPolygonF poly;
|
||||||
|
poly.reserve(pointCount);
|
||||||
|
for (int i = 0; i < pointCount; ++i)
|
||||||
|
poly << points[i];
|
||||||
|
|
||||||
|
strokePolygon(poly, mode != PolylineMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxfPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
|
||||||
|
{
|
||||||
|
qreal font_size = textItem.font().pointSizeF();
|
||||||
|
if (font_size < 0)
|
||||||
|
font_size = textItem.font().pixelSize();
|
||||||
|
|
||||||
|
// Rotation: extract the angle baked into the world transform by
|
||||||
|
// mapping a unit vector, the same approach used to compute rotation
|
||||||
|
// for the existing element-text DXF export in exportdialog.cpp.
|
||||||
|
QPointF origin = m_world_transform.map(p);
|
||||||
|
QPointF right = m_world_transform.map(p + QPointF(1, 0));
|
||||||
|
qreal angle = qRadiansToDegrees(qAtan2(-(right.y() - origin.y()), right.x() - origin.x()));
|
||||||
|
|
||||||
|
Createdxf::drawText(m_filepath, textItem.text(), toDxf(p), font_size,
|
||||||
|
angle, Createdxf::dxfColor(m_pen), 0.72);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief DxfPaintEngine::drawPixmap
|
||||||
|
Not implemented in v1 - out of scope per the design note in the
|
||||||
|
header (CrossRefItem, the only item exported through this engine so
|
||||||
|
far, never draws a pixmap). qWarning() rather than a hard failure, so
|
||||||
|
an item that does call this in the future degrades to "one entity
|
||||||
|
missing" instead of crashing the whole export.
|
||||||
|
*/
|
||||||
|
void DxfPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
|
||||||
|
{
|
||||||
|
Q_UNUSED(r)
|
||||||
|
Q_UNUSED(pm)
|
||||||
|
Q_UNUSED(sr)
|
||||||
|
qWarning() << "DxfPaintEngine::drawPixmap: not supported, entity skipped";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief DxfPaintDevice::DxfPaintDevice
|
||||||
|
@param filepath path of the already-open DXF file (see
|
||||||
|
Createdxf::dxfBegin()/dxfEnd() - this device only appends entities in
|
||||||
|
between, it does not open/close the file itself).
|
||||||
|
*/
|
||||||
|
DxfPaintDevice::DxfPaintDevice(const QString &filepath) :
|
||||||
|
QPaintDevice(),
|
||||||
|
m_filepath(filepath),
|
||||||
|
m_engine(new DxfPaintEngine(filepath))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DxfPaintDevice::~DxfPaintDevice()
|
||||||
|
{
|
||||||
|
delete m_engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPaintEngine *DxfPaintDevice::paintEngine() const
|
||||||
|
{
|
||||||
|
return m_engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DxfPaintDevice::metric(PaintDeviceMetric metric) const
|
||||||
|
{
|
||||||
|
// A DXF document has no pixel grid or physical size in the sense
|
||||||
|
// QPaintDevice::metric() expects - large-but-finite values here are
|
||||||
|
// enough to keep QPainter's own bookkeeping (e.g. clip region setup)
|
||||||
|
// from misbehaving; nothing in this engine actually consults them.
|
||||||
|
switch (metric)
|
||||||
|
{
|
||||||
|
case PdmWidth:
|
||||||
|
case PdmHeight:
|
||||||
|
return 1000000;
|
||||||
|
case PdmDpiX:
|
||||||
|
case PdmDpiY:
|
||||||
|
case PdmPhysicalDpiX:
|
||||||
|
case PdmPhysicalDpiY:
|
||||||
|
return 96;
|
||||||
|
case PdmWidthMM:
|
||||||
|
case PdmHeightMM:
|
||||||
|
return 1000000;
|
||||||
|
case PdmNumColors:
|
||||||
|
return 16777216;
|
||||||
|
case PdmDepth:
|
||||||
|
return 24;
|
||||||
|
case PdmDevicePixelRatio:
|
||||||
|
case PdmDevicePixelRatioScaled:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2006-2026 The QElectroTech Team
|
||||||
|
This file is part of QElectroTech.
|
||||||
|
|
||||||
|
QElectroTech is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
QElectroTech is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifndef DXFPAINTDEVICE_H
|
||||||
|
#define DXFPAINTDEVICE_H
|
||||||
|
|
||||||
|
#include <QPaintDevice>
|
||||||
|
#include <QPaintEngine>
|
||||||
|
#include <QPen>
|
||||||
|
#include <QBrush>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief The DxfPaintEngine class
|
||||||
|
A QPaintEngine that translates the small set of QPainter calls made by
|
||||||
|
QGraphicsItem::paint() implementations (drawLines, drawRects,
|
||||||
|
drawEllipse, drawPolygon/drawPolyline, drawTextItem) into DXF entities
|
||||||
|
written through Createdxf, instead of pixels.
|
||||||
|
|
||||||
|
This exists so that an item's existing, already-correct paint() code
|
||||||
|
can be reused unmodified to produce a DXF export: construct a QPainter
|
||||||
|
on a DxfPaintDevice targeting the item, call painter.begin()/end()
|
||||||
|
around item->paint(&painter, ...), and every primitive the item draws
|
||||||
|
becomes a DXF entity at the item's scene position instead of a pixel
|
||||||
|
on screen.
|
||||||
|
|
||||||
|
Scope (deliberately not the full QPainter surface - only what
|
||||||
|
QElectroTech's own paint() implementations are observed to call):
|
||||||
|
- drawLines -> LINE
|
||||||
|
- drawRects -> LWPOLYLINE (closed 4-point outline; DXF has no
|
||||||
|
filled-rect primitive in the AC1006 dialect this
|
||||||
|
exporter targets, so brush fill is not emitted -
|
||||||
|
see fillPath below)
|
||||||
|
- drawEllipse -> Createdxf::drawArcEllipse (full sweep) or CIRCLE
|
||||||
|
- drawPath -> arcs (from QPainterPath::Arc elements, used by
|
||||||
|
drawArc/drawPie) become a sequence of LINE chords;
|
||||||
|
anything else in the path is flattened to
|
||||||
|
polyline segments via QPainterPath::toSubpathPolygons
|
||||||
|
- drawPolygon -> LWPOLYLINE
|
||||||
|
- drawTextItem -> TEXT (drawText() calls route through this)
|
||||||
|
- fillPath -> same outline-only handling as drawRects; no HATCH
|
||||||
|
support in v1 (see design note in the PR)
|
||||||
|
|
||||||
|
Anything outside this list (images, gradients, etc.) is intentionally
|
||||||
|
unimplemented and asserts in debug builds rather than silently
|
||||||
|
producing an incomplete drawing - callers should know immediately if
|
||||||
|
an item they're exporting uses something this engine doesn't cover
|
||||||
|
yet, rather than getting a DXF file quietly missing content.
|
||||||
|
*/
|
||||||
|
class DxfPaintEngine : public QPaintEngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit DxfPaintEngine(const QString &filepath);
|
||||||
|
|
||||||
|
bool begin(QPaintDevice *pdev) override;
|
||||||
|
bool end() override;
|
||||||
|
void updateState(const QPaintEngineState &state) override;
|
||||||
|
|
||||||
|
void drawLines(const QLineF *lines, int lineCount) override;
|
||||||
|
void drawRects(const QRectF *rects, int rectCount) override;
|
||||||
|
void drawEllipse(const QRectF &rect) override;
|
||||||
|
void drawPath(const QPainterPath &path) override;
|
||||||
|
void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override;
|
||||||
|
void drawTextItem(const QPointF &p, const QTextItem &textItem) override;
|
||||||
|
void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override;
|
||||||
|
|
||||||
|
Type type() const override { return QPaintEngine::User; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointF toDxf(const QPointF &scene_point) const;
|
||||||
|
void strokePolygon(const QPolygonF &poly, bool close);
|
||||||
|
|
||||||
|
QString m_filepath;
|
||||||
|
QTransform m_world_transform;
|
||||||
|
QPen m_pen;
|
||||||
|
QBrush m_brush;
|
||||||
|
QFont m_font;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief The DxfPaintDevice class
|
||||||
|
Pairs with DxfPaintEngine. One instance targets one already-open DXF
|
||||||
|
file (Createdxf::dxfBegin()/dxfEnd() bracket the whole export, same as
|
||||||
|
today - this device only ever appends entities in between).
|
||||||
|
*/
|
||||||
|
class DxfPaintDevice : public QPaintDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit DxfPaintDevice(const QString &filepath);
|
||||||
|
~DxfPaintDevice() override;
|
||||||
|
|
||||||
|
QPaintEngine *paintEngine() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int metric(PaintDeviceMetric metric) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_filepath;
|
||||||
|
DxfPaintEngine *m_engine;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DXFPAINTDEVICE_H
|
||||||
@@ -22,8 +22,10 @@
|
|||||||
#include "exportpropertieswidget.h"
|
#include "exportpropertieswidget.h"
|
||||||
#include "factory/elementpicturefactory.h"
|
#include "factory/elementpicturefactory.h"
|
||||||
#include "qetgraphicsitem/ViewItem/qetgraphicstableitem.h"
|
#include "qetgraphicsitem/ViewItem/qetgraphicstableitem.h"
|
||||||
|
#include "dxfpaintdevice.h"
|
||||||
#include "qetgraphicsitem/conductor.h"
|
#include "qetgraphicsitem/conductor.h"
|
||||||
#include "qetgraphicsitem/conductortextitem.h"
|
#include "qetgraphicsitem/conductortextitem.h"
|
||||||
|
#include "qetgraphicsitem/crossrefitem.h"
|
||||||
#include "qetgraphicsitem/diagramimageitem.h"
|
#include "qetgraphicsitem/diagramimageitem.h"
|
||||||
#include "qetgraphicsitem/diagramtextitem.h"
|
#include "qetgraphicsitem/diagramtextitem.h"
|
||||||
#include "qetgraphicsitem/dynamicelementtextitem.h"
|
#include "qetgraphicsitem/dynamicelementtextitem.h"
|
||||||
@@ -467,6 +469,12 @@ void ExportDialog::generateDxf(
|
|||||||
//as plain QGraphicsTextItem children, so neither cast below picks them
|
//as plain QGraphicsTextItem children, so neither cast below picks them
|
||||||
//up and they were missing from the DXF entirely.
|
//up and they were missing from the DXF entirely.
|
||||||
QList<QGraphicsTextItem *> list_xref_texts;
|
QList<QGraphicsTextItem *> list_xref_texts;
|
||||||
|
//Master-side cross-reference item (the table/cross drawn next to a
|
||||||
|
//report/master element). It paints itself with hand-written
|
||||||
|
//QPainter code across three modes (drawAsCross/drawAsContacts/
|
||||||
|
//drawAsPlcTable), so instead of hand-porting each one it's replayed
|
||||||
|
//through DxfPaintEngine, which reuses paint() unmodified.
|
||||||
|
QList<CrossRefItem *> list_master_xrefs;
|
||||||
QList<QLineF *> list_lines;
|
QList<QLineF *> list_lines;
|
||||||
QList<QRectF *> list_rectangles;
|
QList<QRectF *> list_rectangles;
|
||||||
//QList<QRectF *> list_ellipses;
|
//QList<QRectF *> list_ellipses;
|
||||||
@@ -493,6 +501,8 @@ void ExportDialog::generateDxf(
|
|||||||
}
|
}
|
||||||
} else if (QetGraphicsTableItem *gti = qgraphicsitem_cast<QetGraphicsTableItem *>(qgi)) {
|
} else if (QetGraphicsTableItem *gti = qgraphicsitem_cast<QetGraphicsTableItem *>(qgi)) {
|
||||||
list_tables << gti;
|
list_tables << gti;
|
||||||
|
} else if (CrossRefItem *xref = qgraphicsitem_cast<CrossRefItem *>(qgi)) {
|
||||||
|
list_master_xrefs << xref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -722,6 +732,18 @@ void ExportDialog::generateDxf(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Draw the master-side cross-reference items (table/cross), replaying
|
||||||
|
//their existing paint() unmodified through DxfPaintEngine instead of
|
||||||
|
//hand-porting drawAsCross()/drawAsContacts()/drawAsPlcTable().
|
||||||
|
for (CrossRefItem *xref : std::as_const(list_master_xrefs))
|
||||||
|
{
|
||||||
|
DxfPaintDevice dxf_device(file_path);
|
||||||
|
QPainter painter(&dxf_device);
|
||||||
|
painter.setWorldTransform(xref->sceneTransform());
|
||||||
|
xref->paintForExport(&painter);
|
||||||
|
painter.end();
|
||||||
|
}
|
||||||
|
|
||||||
Createdxf::dxfEnd(file_path);
|
Createdxf::dxfEnd(file_path);
|
||||||
|
|
||||||
saveReloadDiagramParameters(diagram, false);
|
saveReloadDiagramParameters(diagram, false);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <QGraphicsObject>
|
#include <QGraphicsObject>
|
||||||
#include <QMultiMap>
|
#include <QMultiMap>
|
||||||
|
#include <QStyleOptionGraphicsItem>
|
||||||
|
|
||||||
class Element;
|
class Element;
|
||||||
class DynamicElementTextItem;
|
class DynamicElementTextItem;
|
||||||
@@ -89,6 +90,19 @@ class CrossRefItem : public QGraphicsObject
|
|||||||
void updateLabel();
|
void updateLabel();
|
||||||
void autoPos();
|
void autoPos();
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// DXF export: replay this item's paint() on an arbitrary QPainter
|
||||||
|
/// (e.g. one targeting DxfPaintDevice). paint() itself stays
|
||||||
|
/// protected, as it should for the normal
|
||||||
|
/// QGraphicsScene/QGraphicsView paint contract - this is a
|
||||||
|
/// deliberate, narrow escape hatch for exporters, not a general
|
||||||
|
/// relaxation of that contract.
|
||||||
|
void paintForExport(QPainter *painter)
|
||||||
|
{
|
||||||
|
QStyleOptionGraphicsItem option;
|
||||||
|
paint(painter, &option, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool sceneEvent(QEvent *event) override;
|
bool sceneEvent(QEvent *event) override;
|
||||||
void paint(QPainter *painter,
|
void paint(QPainter *painter,
|
||||||
|
|||||||
@@ -89,6 +89,9 @@ class DynamicElementTextItem : public DiagramTextItem
|
|||||||
Element *parentElement() const;
|
Element *parentElement() const;
|
||||||
/// PDF export: slave cross-reference text item ("(folio-pos)") and its master target.
|
/// PDF export: slave cross-reference text item ("(folio-pos)") and its master target.
|
||||||
QGraphicsTextItem *slaveXrefItem() const { return m_slave_Xref_item; }
|
QGraphicsTextItem *slaveXrefItem() const { return m_slave_Xref_item; }
|
||||||
|
/// DXF export: the master-side cross-reference item (the table/cross
|
||||||
|
/// drawn next to a report/master element), if this text item has one.
|
||||||
|
CrossRefItem *masterXrefItem() const { return m_Xref_item; }
|
||||||
Element *masterElement() const { return m_master_element.data(); }
|
Element *masterElement() const { return m_master_element.data(); }
|
||||||
ElementTextItemGroup *parentGroup() const;
|
ElementTextItemGroup *parentGroup() const;
|
||||||
Element *elementUseForInfo() const;
|
Element *elementUseForInfo() const;
|
||||||
|
|||||||
Reference in New Issue
Block a user