diff --git a/src/imageresultitem.cpp b/src/imageresultitem.cpp index 2e0ace80..15359360 100644 --- a/src/imageresultitem.cpp +++ b/src/imageresultitem.cpp @@ -1,98 +1,104 @@ /* This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2012 Martin Kuettler */ #include "imageresultitem.h" #include "commandentry.h" #include "lib/imageresult.h" #include "lib/epsresult.h" #include #include #include ImageResultItem::ImageResultItem(QGraphicsObject* parent, Cantor::Result* result) : WorksheetImageItem(parent), ResultItem(result) { update(); } double ImageResultItem::setGeometry(double x, double y, double w) { Q_UNUSED(w); setPos(x,y); return height(); } void ImageResultItem::populateMenu(QMenu* menu, QPointF pos) { ResultItem::addCommonActions(this, menu); menu->addSeparator(); qDebug() << "populate Menu"; emit menuCreated(menu, mapToParent(pos)); } void ImageResultItem::update() { Q_ASSERT(m_result->type() == Cantor::ImageResult::Type || m_result->type() == Cantor::EpsResult::Type); switch(m_result->type()) { case Cantor::ImageResult::Type: - setImage(m_result->data().value()); + { + QSize displaySize = static_cast(m_result)->displaySize(); + if (displaySize.isValid()) + setImage(m_result->data().value(), displaySize); + else + setImage(m_result->data().value()); + } break; case Cantor::EpsResult::Type: setEps(m_result->data().toUrl()); break; default: break; } } QRectF ImageResultItem::boundingRect() const { return QRectF(0, 0, width(), height()); } double ImageResultItem::width() const { return WorksheetImageItem::width(); } double ImageResultItem::height() const { return WorksheetImageItem::height(); } void ImageResultItem::saveResult() { Cantor::Result* res = result(); const QString& filename=QFileDialog::getSaveFileName(worksheet()->worksheetView(), i18n("Save result"), QString(), res->mimeType()); qDebug()<<"saving result to "<save(filename); } void ImageResultItem::deleteLater() { WorksheetImageItem::deleteLater(); } EpsRenderer* ImageResultItem::epsRenderer() { return qobject_cast(scene())->epsRenderer(); } diff --git a/src/lib/imageresult.cpp b/src/lib/imageresult.cpp index 16ea92aa..8402c2d8 100644 --- a/src/lib/imageresult.cpp +++ b/src/lib/imageresult.cpp @@ -1,172 +1,185 @@ /* This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #include "imageresult.h" using namespace Cantor; #include #include #include #include #include #include class Cantor::ImageResultPrivate { public: ImageResultPrivate() = default; QUrl url; QImage img; QString alt; + QSize displaySize; }; ImageResult::ImageResult(const QUrl &url, const QString& alt) : d(new ImageResultPrivate) { d->url=url; d->alt=alt; } Cantor::ImageResult::ImageResult(const QImage& image, const QString& alt) : d(new ImageResultPrivate) { d->img=image; d->alt=alt; QTemporaryFile imageFile; imageFile.setAutoRemove(false); if (imageFile.open()) { d->img.save(imageFile.fileName(), "PNG"); d->url = QUrl::fromLocalFile(imageFile.fileName()); } } ImageResult::~ImageResult() { delete d; } QString ImageResult::toHtml() { return QStringLiteral("\"%2\"/").arg(d->url.toLocalFile(), d->alt); } QString ImageResult::toLatex() { return QStringLiteral(" \\begin{center} \n \\includegraphics[width=12cm]{%1} \n \\end{center}").arg(d->url.fileName()); } QVariant ImageResult::data() { if(d->img.isNull()) d->img.load(d->url.toLocalFile()); return QVariant(d->img); } QUrl ImageResult::url() { return d->url; } int ImageResult::type() { return ImageResult::Type; } QString ImageResult::mimeType() { const QList formats=QImageWriter::supportedImageFormats(); QString mimetype; foreach(const QByteArray& format, formats) { mimetype+=QLatin1String("image/"+format.toLower()+' '); } qDebug()<<"type: "<url.fileName()); if (!d->alt.isEmpty()) e.appendChild(doc.createTextNode(d->alt)); qDebug()<<"done"; return e; } QJsonValue Cantor::ImageResult::toJupyterJson() { QJsonObject root; if (executionIndex() != -1) { root.insert(QLatin1String("output_type"), QLatin1String("execute_result")); root.insert(QLatin1String("execution_count"), executionIndex()); } else root.insert(QLatin1String("output_type"), QLatin1String("display_data")); QJsonObject data; data.insert(QLatin1String("text/plain"), toJupyterMultiline(d->alt)); QImage image; if (d->img.isNull()) image.load(d->url.toLocalFile()); else image = d->img; QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); data.insert(QLatin1String("image/png"), QString::fromLatin1(ba.toBase64())); root.insert(QLatin1String("data"), data); QJsonObject metadata(jupyterMetadata()); - QJsonObject size; - size.insert(QLatin1String("width"), image.size().width()); - size.insert(QLatin1String("height"), image.size().height()); - metadata.insert(QLatin1String("image/png"), size); + if (d->displaySize.isValid()) + { + QJsonObject size; + size.insert(QLatin1String("width"), displaySize().width()); + size.insert(QLatin1String("height"), displaySize().height()); + metadata.insert(QLatin1String("image/png"), size); + } root.insert(QLatin1String("metadata"), metadata); return root; } void ImageResult::saveAdditionalData(KZip* archive) { archive->addLocalFile(d->url.toLocalFile(), d->url.fileName()); } void ImageResult::save(const QString& filename) { //load into memory and let Qt save it, instead of just copying d->url //to give possibility to convert file format QImage img=data().value(); img.save(filename); } +QSize Cantor::ImageResult::displaySize() +{ + return d->displaySize; +} + +void Cantor::ImageResult::setDisplaySize(QSize size) +{ + d->displaySize = size; +} diff --git a/src/lib/imageresult.h b/src/lib/imageresult.h index 9cd347d7..3366a940 100644 --- a/src/lib/imageresult.h +++ b/src/lib/imageresult.h @@ -1,61 +1,65 @@ /* This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #ifndef _IMAGERESULT_H #define _IMAGERESULT_H #include "result.h" #include +#include class QImage; namespace Cantor { class ImageResultPrivate; class CANTOR_EXPORT ImageResult : public Result { public: enum{Type=2}; explicit ImageResult( const QUrl& url, const QString& alt=QString()); explicit ImageResult( const QImage& image, const QString& alt=QString()); ~ImageResult() override; QString toHtml() override; QString toLatex() override; QVariant data() override; QUrl url() override; int type() override; QString mimeType() override; + QSize displaySize(); + void setDisplaySize(QSize size); + QDomElement toXml(QDomDocument& doc) override; QJsonValue toJupyterJson() override; void saveAdditionalData(KZip* archive) override; void save(const QString& filename) override; private: ImageResultPrivate* d; }; } #endif /* _IMAGERESULT_H */ diff --git a/src/loadedexpression.cpp b/src/loadedexpression.cpp index 49516d82..25607b24 100644 --- a/src/loadedexpression.cpp +++ b/src/loadedexpression.cpp @@ -1,289 +1,294 @@ /* This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #include "loadedexpression.h" #include "jupyterutils.h" #include "lib/imageresult.h" #include "lib/epsresult.h" #include "lib/textresult.h" #include "lib/latexresult.h" #include "lib/animationresult.h" #include "lib/latexrenderer.h" #include "lib/mimeresult.h" #include "lib/htmlresult.h" #include #include #include #include #include #include #include #include LoadedExpression::LoadedExpression( Cantor::Session* session ) : Cantor::Expression( session, false, -1) { } void LoadedExpression::interrupt() { //Do nothing } void LoadedExpression::evaluate() { //Do nothing } void LoadedExpression::loadFromXml(const QDomElement& xml, const KZip& file) { setCommand(xml.firstChildElement(QLatin1String("Command")).text()); const QDomNodeList& results = xml.elementsByTagName(QLatin1String("Result")); for (int i = 0; i < results.size(); i++) { const QDomElement& resultElement = results.at(i).toElement(); const QString& type = resultElement.attribute(QLatin1String("type")); qDebug() << "type" << type; if ( type == QLatin1String("text")) { const QString& format = resultElement.attribute(QLatin1String("format")); Cantor::TextResult* result = new Cantor::TextResult(resultElement.text()); if (format == QLatin1String("latex")) result->setFormat(Cantor::TextResult::LatexFormat); addResult(result); } else if (type == QLatin1String("mime")) { const QDomElement& resultElement = results.at(i).toElement(); const QString& mimeType = resultElement.attribute(QLatin1String("mimeType")); bool withPlain = resultElement.attribute(QLatin1String("withPlain")).toInt(); QString plain; if (withPlain) plain = resultElement.firstChildElement(QLatin1String("Plain")).text(); const QString& content = resultElement.firstChildElement(QLatin1String("Content")).text(); QJsonDocument jsonDoc = QJsonDocument::fromJson(content.toUtf8());; const QJsonValue& value = jsonDoc.object().value(QLatin1String("content")); addResult(new Cantor::MimeResult(plain, value, mimeType)); } else if (type == QLatin1String("html")) { const QString& formatString = resultElement.attribute(QLatin1String("showCode")); Cantor::HtmlResult::Format format = Cantor::HtmlResult::Html; if (formatString == QLatin1String("htmlSource")) format = Cantor::HtmlResult::HtmlSource; else if (formatString == QLatin1String("plain")) format = Cantor::HtmlResult::PlainAlternative; const QString& plain = resultElement.firstChildElement(QLatin1String("Plain")).text(); const QString& html = resultElement.firstChildElement(QLatin1String("Html")).text(); Cantor::HtmlResult* result = new Cantor::HtmlResult(html, plain); result->setFormat(format); addResult(result); } else if (type == QLatin1String("image") || type == QLatin1String("latex") || type == QLatin1String("animation")) { const KArchiveEntry* imageEntry=file.directory()->entry(resultElement.attribute(QLatin1String("filename"))); if (imageEntry&&imageEntry->isFile()) { const KArchiveFile* imageFile=static_cast(imageEntry); QString dir=QStandardPaths::writableLocation(QStandardPaths::TempLocation); imageFile->copyTo(dir); QUrl imageUrl = QUrl::fromLocalFile(QDir(dir).absoluteFilePath(imageFile->name())); if(type==QLatin1String("latex")) { addResult(new Cantor::LatexResult(resultElement.text(), imageUrl)); }else if(type==QLatin1String("animation")) { addResult(new Cantor::AnimationResult(imageUrl)); }else if(imageFile->name().endsWith(QLatin1String(".eps"))) { addResult(new Cantor::EpsResult(imageUrl)); }else { addResult(new Cantor::ImageResult(imageUrl, resultElement.text())); } } } } const QDomElement& errElem = xml.firstChildElement(QLatin1String("Error")); if (!errElem.isNull()) { setErrorMessage(errElem.text()); setStatus(Error); } else setStatus(Done); } void LoadedExpression::loadFromJupyter(const QJsonObject& cell) { setCommand(JupyterUtils::getSource(cell)); const QJsonValue idObject = cell.value(QLatin1String("execution_count")); if (!idObject.isUndefined() && !idObject.isNull()) setId(idObject.toInt()); const QJsonArray& outputs = cell.value(QLatin1String("outputs")).toArray(); for (QJsonArray::const_iterator iter = outputs.begin(); iter != outputs.end(); iter++) { if (!JupyterUtils::isJupyterOutput(*iter)) continue; const QJsonObject& output = iter->toObject(); const QString& outputType = JupyterUtils::getOutputType(output); if (JupyterUtils::isJupyterTextOutput(output)) { const QString& text = JupyterUtils::fromJupyterMultiline(output.value(QLatin1String("text"))); addResult(new Cantor::TextResult(text)); } else if (JupyterUtils::isJupyterErrorOutput(output)) { const QJsonArray& tracebackLineArray = output.value(QLatin1String("traceback")).toArray(); QString traceback; // Looks like the traceback in Jupyter joined with '\n', no '' // So, manually add it for (const QJsonValue& line : tracebackLineArray) traceback += line.toString() + QLatin1Char('\n'); traceback.chop(1); // IPython returns error with terminal colors, we handle it here, but should we? static const QChar ESC(0x1b); traceback.remove(QRegExp(QString(ESC)+QLatin1String("\\[[0-9;]*m"))); setErrorMessage(traceback); } else if (JupyterUtils::isJupyterDisplayOutput(output) || JupyterUtils::isJupyterExecutionResult(output)) { const QJsonObject& data = output.value(QLatin1String("data")).toObject(); QJsonObject metadata = JupyterUtils::getMetadata(output); const QString& text = JupyterUtils::fromJupyterMultiline(data.value(JupyterUtils::textMime)); const QString& mainKey = JupyterUtils::mainBundleKey(data); Cantor::Result* result = nullptr; if (mainKey == JupyterUtils::gifMime) { const QByteArray& bytes = QByteArray::fromBase64(data.value(mainKey).toString().toLatin1()); QTemporaryFile file; file.setAutoRemove(false); file.open(); file.write(bytes); file.close(); result = new Cantor::AnimationResult(QUrl::fromLocalFile(file.fileName()), text); } else if (mainKey == JupyterUtils::textMime) { result = new Cantor::TextResult(text); } else if (mainKey == JupyterUtils::htmlMime) { const QString& html = JupyterUtils::fromJupyterMultiline(data.value(JupyterUtils::htmlMime)); // Some backends places gif animation in hmlt (img tag), for example, Sage if (JupyterUtils::isGifHtml(html)) { result = new Cantor::AnimationResult(JupyterUtils::loadGifHtml(html), text); } else { result = new Cantor::HtmlResult(html, text); } } else if (mainKey == JupyterUtils::latexMime) { QString latex = JupyterUtils::fromJupyterMultiline(data.value(mainKey)); QScopedPointer renderer(new Cantor::LatexRenderer(this)); renderer->setLatexCode(latex); renderer->setEquationOnly(false); renderer->setMethod(Cantor::LatexRenderer::LatexMethod); renderer->renderBlocking(); result = new Cantor::LatexResult(latex, QUrl::fromLocalFile(renderer->imagePath()), text); // If we have failed to render LaTeX i think Cantor should show the latex code at least if (!renderer->renderingSuccessful()) static_cast(result)->showCode(); } // So this is image else if (JupyterUtils::imageKeys(data).contains(mainKey)) { - QImage image = JupyterUtils::loadImage(data, mainKey); + const QImage& image = JupyterUtils::loadImage(data, mainKey); + result = new Cantor::ImageResult(image, text); const QJsonValue size = metadata.value(mainKey); if (size.isObject()) { int w = size.toObject().value(QLatin1String("width")).toInt(-1); int h = size.toObject().value(QLatin1String("height")).toInt(-1); if (w != -1 && h != -1) - image = image.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - - // Remove size information, because we don't need it - metadata.remove(mainKey); + { + static_cast(result)->setDisplaySize(QSize(w, h)); + // Remove size information, because we don't need it after setting display size + // Also, we encode image to 'image/png' on saving as .ipynb, even original image don't png + // So, without removing the size info here, after loading for example 'image/tiff' to Cantor from .ipynb and saving the worksheet + // (which means, that the image will be saved as png and not as tiff). + // We will have outdated key 'image/tiff' in metadata. + metadata.remove(mainKey); + } } - result = new Cantor::ImageResult(image, text); } else if (data.keys().size() == 1 && data.keys()[0] == JupyterUtils::textMime) result = new Cantor::TextResult(text); // Cantor don't know, how handle this, so pack into mime container result else if (data.keys().count() > 0) { qDebug() << "Found unsupported " << outputType << "result with mimes" << data.keys() << ", so add them to mime container result"; QString key; if (data.keys().contains(JupyterUtils::textMime) && data.keys().count() > 1) if (data.keys()[0] == JupyterUtils::textMime) key = data.keys()[1]; else key = data.keys()[0]; else key = data.keys()[0]; result = new Cantor::MimeResult(text, data.value(key), key); } if (result) { result->setJupyterMetadata(metadata); int resultIndex = output.value(QLatin1String("execution_count")).toInt(-1); if (resultIndex != -1) result->setExecutionIndex(resultIndex); addResult(result); } } } if (errorMessage().isEmpty()) setStatus(Done); else setStatus(Error); } diff --git a/src/worksheetimageitem.cpp b/src/worksheetimageitem.cpp index 0c2ed90f..30dfe4f8 100644 --- a/src/worksheetimageitem.cpp +++ b/src/worksheetimageitem.cpp @@ -1,165 +1,172 @@ /* This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2012 Martin Kuettler */ #include "worksheetimageitem.h" #include "worksheet.h" #include #include #include #include #include #include WorksheetImageItem::WorksheetImageItem(QGraphicsObject* parent) : QGraphicsObject(parent) { connect(this, SIGNAL(menuCreated(QMenu*,QPointF)), parent, SLOT(populateMenu(QMenu*,QPointF)), Qt::DirectConnection); m_maxWidth = 0; } WorksheetImageItem::~WorksheetImageItem() { if (worksheet() && m_maxWidth > 0 && width() > m_maxWidth) worksheet()->removeProtrusion(width() - m_maxWidth); } int WorksheetImageItem::type() const { return Type; } bool WorksheetImageItem::imageIsValid() { return !m_pixmap.isNull(); } qreal WorksheetImageItem::setGeometry(qreal x, qreal y, qreal w, bool centered) { if (width() <= w && centered) { setPos(x + w/2 - width()/2, y); } else { setPos(x, y); if (m_maxWidth < width()) worksheet()->updateProtrusion(width() - m_maxWidth, width() - w); else worksheet()->addProtrusion(width() - w); } m_maxWidth = w; return height(); } qreal WorksheetImageItem::height() const { return m_size.height(); } qreal WorksheetImageItem::width() const { return m_size.width(); } QSizeF WorksheetImageItem::size() { return m_size; } void WorksheetImageItem::setSize(QSizeF size) { qreal oldProtrusion = x() + m_size.width() - m_maxWidth; qreal newProtrusion = x() + size.width() - m_maxWidth; if (oldProtrusion > 0) { if (newProtrusion > 0) worksheet()->updateProtrusion(oldProtrusion, newProtrusion); else worksheet()->removeProtrusion(oldProtrusion); } else { if (newProtrusion > 0) worksheet()->addProtrusion(newProtrusion); } m_size = size; } QSize WorksheetImageItem::imageSize() { return m_pixmap.size(); } QRectF WorksheetImageItem::boundingRect() const { return QRectF(QPointF(0, 0), m_size); } #include void WorksheetImageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); painter->drawPixmap(QRectF(QPointF(0,0), m_size), m_pixmap, m_pixmap.rect()); } void WorksheetImageItem::setEps(const QUrl& url) { const QImage img = worksheet()->epsRenderer()->renderToImage(url, &m_size); m_pixmap = QPixmap::fromImage(img.convertToFormat(QImage::Format_ARGB32)); } void WorksheetImageItem::setImage(QImage img) { m_pixmap = QPixmap::fromImage(img); setSize(m_pixmap.size()); } +void WorksheetImageItem::setImage(QImage img, QSize displaySize) +{ + m_pixmap = QPixmap::fromImage(img); + m_pixmap = m_pixmap.scaled(displaySize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + setSize(m_pixmap.size()); +} + void WorksheetImageItem::setPixmap(QPixmap pixmap) { m_pixmap = pixmap; } QPixmap WorksheetImageItem::pixmap() const { return m_pixmap; } void WorksheetImageItem::populateMenu(QMenu* menu, QPointF pos) { emit menuCreated(menu, mapToParent(pos)); } void WorksheetImageItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { QMenu *menu = worksheet()->createContextMenu(); populateMenu(menu, event->pos()); menu->popup(event->screenPos()); } Worksheet* WorksheetImageItem::worksheet() { return qobject_cast(scene()); } diff --git a/src/worksheetimageitem.h b/src/worksheetimageitem.h index 8a787366..d6fd1ed1 100644 --- a/src/worksheetimageitem.h +++ b/src/worksheetimageitem.h @@ -1,79 +1,80 @@ /* This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2012 Martin Kuettler */ #ifndef WORKSHEETIMAGEITEM_H #define WORKSHEETIMAGEITEM_H #include #include class Worksheet; class QImage; class QGraphicsSceneContextMenuEvent; class QMenu; class WorksheetImageItem : public QGraphicsObject { Q_OBJECT public: explicit WorksheetImageItem(QGraphicsObject* parent); ~WorksheetImageItem() override; enum {Type = UserType + 101}; int type() const override; bool imageIsValid(); virtual qreal setGeometry(qreal x, qreal y, qreal w, bool centered=false); qreal height() const; qreal width() const; QSizeF size(); void setSize(QSizeF size); QSize imageSize(); QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; void setEps(const QUrl &url); void setImage(QImage img); + void setImage(QImage img, QSize displaySize); void setPixmap(QPixmap pixmap); QPixmap pixmap() const; virtual void populateMenu(QMenu* menu, QPointF pos); Worksheet* worksheet(); Q_SIGNALS: void sizeChanged(); void menuCreated(QMenu*, QPointF); protected: void contextMenuEvent(QGraphicsSceneContextMenuEvent*) override; private: QPixmap m_pixmap; QSizeF m_size; qreal m_maxWidth; }; #endif //WORKSHEETIMAGEITEM_H