V6.7.4 release

This commit is contained in:
2026-07-16 15:35:29 +08:00
parent 808a38628e
commit 6425b11a96
15 changed files with 3734 additions and 3068 deletions
+89 -12
View File
@@ -9,12 +9,16 @@
#include <QFile>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QXmlStreamAttributes>
#include <QDebug>
#include <QImage>
#include <QPainter>
#include <QScopedPointer>
#include <cmath>
#include <unistd.h>
#include "../Printsetting.h"
namespace {
@@ -99,6 +103,7 @@ bool parseVbFileElement(QXmlStreamReader& reader, PicScene* scene)
SourceType sourceType = sourceTypeFromAttrs(itemAttrs);
PicTextItem* item = createSceneItem(sourceType);
if (!item) {
// 单个图元损坏不应影响整个文件的其余内容加载。
qWarning() << "PicSceneLoader: skip invalid VBfile item";
return true;
}
@@ -121,26 +126,36 @@ bool parseVbFileElement(QXmlStreamReader& reader, PicScene* scene)
namespace PicSceneLoader {
PicScene* createSceneFromFile(const QString& xmlFilename)
bool loadSceneFromFile(const QString& xmlFilename, PicScene* targetScene, PrintSettingModel* outPrintSettings)
{
if (!targetScene) {
qWarning() << "PicSceneLoader: targetScene is null";
return false;
}
QFile file(xmlFilename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "PicSceneLoader: failed to open" << xmlFilename << file.errorString();
return nullptr;
return false;
}
QXmlStreamReader reader(&file);
PicScene* scene = new PicScene();
PrintSettingModel printSettings;
// Widget 对 scene 持有长期信号和 view 绑定,因此加载时必须复用现有 scene。
targetScene->clear();
while (!reader.atEnd()) {
reader.readNext();
if (reader.isStartElement() && reader.name() == "VBfiles") {
while (reader.readNextStartElement()) {
if (reader.name() == "VBfile") {
if (!parseVbFileElement(reader, scene)) {
delete scene;
return nullptr;
if (!parseVbFileElement(reader, targetScene)) {
return false;
}
} else if (reader.name() == "PrintSettings") {
PrintSettingModel::loadFromXml(printSettings, reader.attributes());
reader.skipCurrentElement();
} else {
reader.skipCurrentElement();
}
@@ -151,17 +166,21 @@ PicScene* createSceneFromFile(const QString& xmlFilename)
if (reader.hasError()) {
qWarning() << "PicSceneLoader: XML parse error" << reader.errorString()
<< "at" << reader.lineNumber() << ":" << reader.columnNumber();
delete scene;
return nullptr;
return false;
}
return scene;
if (outPrintSettings) {
*outPrintSettings = printSettings;
}
return true;
}
QImage previewSceneFromFile(const QString &xmlFilename)
{
QScopedPointer<PicScene> scene(createSceneFromFile(xmlFilename));
if (!scene) {
// 预览刻意复用与正常打开相同的加载路径,避免两套逻辑逐渐产生偏差。
QScopedPointer<PicScene> scene(new PicScene());
if (!loadSceneFromFile(xmlFilename, scene.data(), nullptr)) {
return QImage();
}
@@ -179,10 +198,68 @@ QImage previewSceneFromFile(const QString &xmlFilename)
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
// 以 itemsBoundingRect 作为渲染源区域,预览图会裁剪到实际内容,而不是整个 sceneRect。
scene->render(&painter, QRectF(QPointF(0, 0), imageSize), contentRect);
painter.end();
return image;
}
} // namespace PicSceneLoader
bool saveSceneToXmlFile(const QString &xmlFilename, PicScene *scene, const PrintSettingModel *printSettings)
{
if (!scene)
return false;
QFile file(xmlFilename);
if (!file.open(QFile::WriteOnly | QFile::Text)) {
qDebug() << QString("Cannot write file %1(%2).").arg(xmlFilename).arg(file.errorString());
return false;
}
QXmlStreamWriter writer(&file);
writer.setAutoFormatting(true);
writer.writeStartDocument("1.0", true);
writer.writeComment(QString::fromLocal8Bit("Vernon Bach graphic scene file"));
writer.writeDTD(QString::fromLocal8Bit("<!DOCTYPE VBfiles [ <!ELEMENT VBfiles (VBfile) > <!ELEMENT VBfile (pointer_x,pointer_y,html_src)> <!ELEMENT pointer_x (#PCDATA)><!ELEMENT pointer_y (#PCDATA)><!ELEMENT html_src (#PCDATA)> ]>"));
writer.writeStartElement("VBfiles");
writer.writeAttribute("Version", "1.0");
QList<QGraphicsItem *> items = scene->items();
foreach (QGraphicsItem *item, items) {
PicTextItem *picText = qgraphicsitem_cast<PicTextItem*>(item);
if (picText == nullptr)
continue;
writer.writeStartElement("VBfile");
writer.writeTextElement(QString::fromLocal8Bit("pointer_x"), QString::fromLocal8Bit("%1").arg(picText->pos().x()));
writer.writeTextElement(QString::fromLocal8Bit("pointer_y"), QString::fromLocal8Bit("%1").arg(picText->pos().y()));
writer.writeTextElement(QString::fromLocal8Bit("Condition"), QString::fromLocal8Bit("%1").arg(picText->SpecialConditions ? 1 : 0));
writer.writeTextElement(QString::fromLocal8Bit("CatchStartValue"), QString::fromLocal8Bit("%1").arg(picText->CatchCnt1StartValue));
writer.writeTextElement(QString::fromLocal8Bit("CatchValue"), QString::fromLocal8Bit("%1").arg(picText->CatchCnt1CurrValue));
QXmlStreamAttributes attrs;
attrs.append("sourceType", QString::number(picText->sourceType()));
if (picText->sourceType() != SourceType::UNKNOWN) {
picText->saveAttrs(attrs);
}
writer.writeStartElement("itemAttrs");
writer.writeAttributes(attrs);
writer.writeEndElement();
writer.writeTextElement(QString::fromLocal8Bit("html_src"), QString::fromLocal8Bit("my_src"));
writer.writeCDATA(picText->toHtml());
writer.writeEndElement();
}
if (printSettings) {
writer.writeEmptyElement("PrintSettings");
PrintSettingModel::saveToXml(*printSettings, writer);
}
writer.writeEndElement();
writer.writeEndDocument();
fsync(file.handle());
file.close();
return true;
}
} // namespace PicSceneLoader