<commit_before>//==============================================================================
// Single cell simulation graph panels widget
//==============================================================================

#include "singlecellsimulationgraphpanel.h"
#include "singlecellsimulationgraphpanels.h"

//==============================================================================

#include <QSettings>
#include <QWheelEvent>

//==============================================================================

namespace OpenCOR {
namespace SingleCellSimulation {

//==============================================================================

SingleCellSimulationGraphPanels::SingleCellSimulationGraphPanels(const QString &pName,
                                                                 QWidget *pParent) :
    QSplitter(Qt::Vertical, pParent),
    CommonWidget(pName, this, pParent)
{
}

//==============================================================================

static const QString SettingsGraphPanelsCount = "GraphPanelsCount";

//==============================================================================

void SingleCellSimulationGraphPanels::loadSettings(QSettings *pSettings)
{
    // Let the user know of a few default things about ourselves by emitting a
    // few signals

    emit removeGraphPanelsEnabled(false);

    // Retrieve the number of graph panels and create the corresponding number
    // of graphs

    int graphPanelsCount = pSettings->value(SettingsGraphPanelsCount, 1).toInt();

    if (!graphPanelsCount)
        // For some reason, the settings for the number of graph panels to be
        // created got messed up, so...

        graphPanelsCount = 1;

    for (int i = 0; i < graphPanelsCount; ++i)
        addGraphPanel();

    // Select the first graph panel

    qobject_cast<SingleCellSimulationGraphPanel *>(widget(0))->setActive(true);
}

//==============================================================================

void SingleCellSimulationGraphPanels::saveSettings(QSettings *pSettings) const
{
    // Keep track of the number of graph panels

    pSettings->setValue(SettingsGraphPanelsCount, count());
}

//==============================================================================

void SingleCellSimulationGraphPanels::wheelEvent(QWheelEvent *pEvent)
{
    // Default handling of the event

    QSplitter::wheelEvent(pEvent);

    // Select the previous/next graph panel, if any

    if (pEvent->delta())
    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellSimulationGraphPanel *graphPanel = qobject_cast<SingleCellSimulationGraphPanel *>(widget(i));

        if (graphPanel->isActive()) {
            // We are dealing with the currently active graph panel, so
            // inactivate it and activate either its predecessor or successor

            graphPanel->setActive(false);

            int shift = 1;

#ifdef Q_WS_MAC
    #ifdef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER
            // From version 10.7 of Mac OS X, the scrolling works the other way
            // round, so...

            shift = -1;
    #endif
#endif

            i += (pEvent->delta() < 0)?shift:-shift;

            if (i < 0)
                i = 0;
            else if (i == iMax)
                i = iMax-1;

            qobject_cast<SingleCellSimulationGraphPanel *>(widget(i))->setActive(true);

            break;
        }
    }
}

//==============================================================================

SingleCellSimulationGraphPanel * SingleCellSimulationGraphPanels::addGraphPanel()
{
    // Keep track of the graph panels' original size

    QList<int> origSizes = sizes();

    // Create a new graph panel

    SingleCellSimulationGraphPanel *res = new SingleCellSimulationGraphPanel(this);

    // Add the graph panel to ourselves

    addWidget(res);

    // Resize the graph panels, thus making sure that their size is what it
    // should be (see issue #58)

    double scalingFactor = (double) (count()-1)/count();

    for (int i = 0, iMax = origSizes.count(); i < iMax; ++i)
        origSizes[i] *= scalingFactor;

    setSizes(origSizes << height()/count());

    // Create a connection to keep track of whenever the graph panel gets
    // activated

    connect(res, SIGNAL(activated(SingleCellSimulationGraphPanel *)),
            this, SLOT(graphPanelActivated(SingleCellSimulationGraphPanel *)));

    // Activate the graph panel

    res->setActive(true);

    // Let people know that we have added a graph panel

    emit grapPanelAdded(res);

    // Let people know whether graph panels can be removed

    emit removeGraphPanelsEnabled(count() > 1);

    // Return our newly created graph panel

    return res;
}

//==============================================================================

void SingleCellSimulationGraphPanels::removeGraphPanel()
{
    if (count() == 1)
        // There is only one graph panel left, so...

        return;

    // Remove the current graph panel

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellSimulationGraphPanel *graphPanel = qobject_cast<SingleCellSimulationGraphPanel *>(widget(i));

        if (graphPanel->isActive()) {
            // We are dealing with the currently active graph panel, so remove
            // it

            graphPanel->hide();

            delete graphPanel;

            // Activate the next graph panel or the last one available, if any

            if (i < count())
                // There is a next graph panel, so activate it

                qobject_cast<SingleCellSimulationGraphPanel *>(widget(i))->setActive(true);
            else
                // We were dealing with the last graph panel, so just activate
                // the new last graph panel

                qobject_cast<SingleCellSimulationGraphPanel *>(widget(count()-1))->setActive(true);

            // We are all done, so...

            break;
        }
    }

    // Let people know that we have removed a graph panel

    emit grapPanelRemoved();

    // Let people know whether graph panels can be removed

    emit removeGraphPanelsEnabled(count() > 1);
}

//==============================================================================

SingleCellSimulationGraphPanel * SingleCellSimulationGraphPanels::activeGraphPanel()
{
    // Return the active graph panel

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellSimulationGraphPanel *graphPanel = qobject_cast<SingleCellSimulationGraphPanel *>(widget(i));

        if (graphPanel->isActive())
            // We found the active graph panel, so...

            return graphPanel;
    }

    // There are no graph panels, so...
    // Note: indeed, since if there is at least one graph panel, then we have an
    //       active graph panel...

    return 0;
}

//==============================================================================

void SingleCellSimulationGraphPanels::graphPanelActivated(SingleCellSimulationGraphPanel *pGraphPanel)
{
    // A graph panel has been activated, so inactivate all the others

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellSimulationGraphPanel *graphPanel = qobject_cast<SingleCellSimulationGraphPanel *>(widget(i));

        if (graphPanel != pGraphPanel)
            // We are not dealing with the graph panel that just got activated,
            // so inactivate it

            graphPanel->setActive(false);
    }
}

//==============================================================================

}   // namespace SingleCellSimulation
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Keeps track of the active graph panel (closes #70).<commit_after>//==============================================================================
// Single cell simulation graph panels widget
//==============================================================================

#include "singlecellsimulationgraphpanel.h"
#include "singlecellsimulationgraphpanels.h"

//==============================================================================

#include <QSettings>
#include <QWheelEvent>

//==============================================================================

namespace OpenCOR {
namespace SingleCellSimulation {

//==============================================================================

SingleCellSimulationGraphPanels::SingleCellSimulationGraphPanels(const QString &pName,
                                                                 QWidget *pParent) :
    QSplitter(Qt::Vertical, pParent),
    CommonWidget(pName, this, pParent)
{
}

//==============================================================================

static const QString SettingsGraphPanelsCount = "GraphPanelsCount";
static const QString SettingsActiveGraphPanel = "ActiveGraphPanel";

//==============================================================================

void SingleCellSimulationGraphPanels::loadSettings(QSettings *pSettings)
{
    // Let the user know of a few default things about ourselves by emitting a
    // few signals

    emit removeGraphPanelsEnabled(false);

    // Retrieve the number of graph panels and create the corresponding number
    // of graphs

    int graphPanelsCount = pSettings->value(SettingsGraphPanelsCount, 1).toInt();

    if (!graphPanelsCount)
        // For some reason, the settings for the number of graph panels to be
        // created got messed up, so...

        graphPanelsCount = 1;

    for (int i = 0; i < graphPanelsCount; ++i)
        addGraphPanel();

    // Select the graph panel that used to be active

    qobject_cast<SingleCellSimulationGraphPanel *>(widget(pSettings->value(SettingsActiveGraphPanel, 0).toInt()))->setActive(true);
}

//==============================================================================

void SingleCellSimulationGraphPanels::saveSettings(QSettings *pSettings) const
{
    // Keep track of the number of graph panels and of which graph panel was the
    // active one

    pSettings->setValue(SettingsGraphPanelsCount, count());

    for (int i = 0, iMax = count(); i < iMax; ++i)
        if (qobject_cast<SingleCellSimulationGraphPanel *>(widget(i))->isActive()) {
            // We found the active graph panel, so...

            pSettings->setValue(SettingsActiveGraphPanel, i);

            break;
        }
}

//==============================================================================

void SingleCellSimulationGraphPanels::wheelEvent(QWheelEvent *pEvent)
{
    // Default handling of the event

    QSplitter::wheelEvent(pEvent);

    // Select the previous/next graph panel, if any

    if (pEvent->delta())
    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellSimulationGraphPanel *graphPanel = qobject_cast<SingleCellSimulationGraphPanel *>(widget(i));

        if (graphPanel->isActive()) {
            // We are dealing with the currently active graph panel, so
            // inactivate it and activate either its predecessor or successor

            graphPanel->setActive(false);

            int shift = 1;

#ifdef Q_WS_MAC
    #ifdef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER
            // From version 10.7 of Mac OS X, the scrolling works the other way
            // round, so...

            shift = -1;
    #endif
#endif

            i += (pEvent->delta() < 0)?shift:-shift;

            if (i < 0)
                i = 0;
            else if (i == iMax)
                i = iMax-1;

            qobject_cast<SingleCellSimulationGraphPanel *>(widget(i))->setActive(true);

            break;
        }
    }
}

//==============================================================================

SingleCellSimulationGraphPanel * SingleCellSimulationGraphPanels::addGraphPanel()
{
    // Keep track of the graph panels' original size

    QList<int> origSizes = sizes();

    // Create a new graph panel

    SingleCellSimulationGraphPanel *res = new SingleCellSimulationGraphPanel(this);

    // Add the graph panel to ourselves

    addWidget(res);

    // Resize the graph panels, thus making sure that their size is what it
    // should be (see issue #58)

    double scalingFactor = (double) (count()-1)/count();

    for (int i = 0, iMax = origSizes.count(); i < iMax; ++i)
        origSizes[i] *= scalingFactor;

    setSizes(origSizes << height()/count());

    // Create a connection to keep track of whenever the graph panel gets
    // activated

    connect(res, SIGNAL(activated(SingleCellSimulationGraphPanel *)),
            this, SLOT(graphPanelActivated(SingleCellSimulationGraphPanel *)));

    // Activate the graph panel

    res->setActive(true);

    // Let people know that we have added a graph panel

    emit grapPanelAdded(res);

    // Let people know whether graph panels can be removed

    emit removeGraphPanelsEnabled(count() > 1);

    // Return our newly created graph panel

    return res;
}

//==============================================================================

void SingleCellSimulationGraphPanels::removeGraphPanel()
{
    if (count() == 1)
        // There is only one graph panel left, so...

        return;

    // Remove the current graph panel

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellSimulationGraphPanel *graphPanel = qobject_cast<SingleCellSimulationGraphPanel *>(widget(i));

        if (graphPanel->isActive()) {
            // We are dealing with the currently active graph panel, so remove
            // it

            graphPanel->hide();

            delete graphPanel;

            // Activate the next graph panel or the last one available, if any

            if (i < count())
                // There is a next graph panel, so activate it

                qobject_cast<SingleCellSimulationGraphPanel *>(widget(i))->setActive(true);
            else
                // We were dealing with the last graph panel, so just activate
                // the new last graph panel

                qobject_cast<SingleCellSimulationGraphPanel *>(widget(count()-1))->setActive(true);

            // We are all done, so...

            break;
        }
    }

    // Let people know that we have removed a graph panel

    emit grapPanelRemoved();

    // Let people know whether graph panels can be removed

    emit removeGraphPanelsEnabled(count() > 1);
}

//==============================================================================

SingleCellSimulationGraphPanel * SingleCellSimulationGraphPanels::activeGraphPanel()
{
    // Return the active graph panel

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellSimulationGraphPanel *graphPanel = qobject_cast<SingleCellSimulationGraphPanel *>(widget(i));

        if (graphPanel->isActive())
            // We found the active graph panel, so...

            return graphPanel;
    }

    // There are no graph panels, so...
    // Note: indeed, since if there is at least one graph panel, then we have an
    //       active graph panel...

    return 0;
}

//==============================================================================

void SingleCellSimulationGraphPanels::graphPanelActivated(SingleCellSimulationGraphPanel *pGraphPanel)
{
    // A graph panel has been activated, so inactivate all the others

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellSimulationGraphPanel *graphPanel = qobject_cast<SingleCellSimulationGraphPanel *>(widget(i));

        if (graphPanel != pGraphPanel)
            // We are not dealing with the graph panel that just got activated,
            // so inactivate it

            graphPanel->setActive(false);
    }
}

//==============================================================================

}   // namespace SingleCellSimulation
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>/*
 * This file is part of openfx-arena <https://github.com/olear/openfx-arena>,
 * Copyright (C) 2015, 2016 FxArena DA
 * Copyright (C) 2016 INRIA
 *
 * openfx-arena is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 *
 * openfx-arena 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 openfx-arena.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
*/

#include <librsvg/rsvg.h>

#ifdef LEGACY
#include "librsvg/rsvg-cairo.h"
#endif

#include "GenericReader.h"
#include "GenericOCIO.h"
#include "ofxsImageEffect.h"

#ifdef OFX_IO_USING_OCIO
#include <OpenColorIO/OpenColorIO.h>
#endif

#define kPluginName "ReadSVG"
#define kPluginGrouping "Image/Readers"
#define kPluginIdentifier "net.fxarena.openfx.ReadSVG"
#define kPluginVersionMajor 2
#define kPluginVersionMinor 0
#define kPluginEvaluation 50

#define kParamDpi "dpi"
#define kParamDpiLabel "DPI"
#define kParamDpiHint "Dots-per-inch (90 is default)"
#define kParamDpiDefault 90

#define kSupportsRGBA true
#define kSupportsRGB false
#define kSupportsAlpha false
#define kSupportsTiles false

class ReadSVGPlugin : public GenericReaderPlugin
{
public:
    ReadSVGPlugin(OfxImageEffectHandle handle, const std::vector<std::string>& extensions);
    virtual ~ReadSVGPlugin();
private:
    virtual bool isVideoStream(const std::string& /*filename*/) OVERRIDE FINAL { return false; }
    virtual void decode(const std::string& filename, OfxTime time, int view, bool isPlayback, const OfxRectI& renderWindow, float *pixelData, const OfxRectI& bounds, OFX::PixelComponentEnum pixelComponents, int pixelComponentCount, int rowBytes) OVERRIDE FINAL;
    virtual bool getFrameBounds(const std::string& filename, OfxTime time, OfxRectI *bounds, double *par, std::string *error,int *tile_width, int *tile_height) OVERRIDE FINAL;
    virtual void onInputFileChanged(const std::string& newFile, bool setColorSpace, OFX::PreMultiplicationEnum *premult, OFX::PixelComponentEnum *components, int *componentCount) OVERRIDE FINAL;
    OFX::IntParam *_dpi;
};

ReadSVGPlugin::ReadSVGPlugin(OfxImageEffectHandle handle, const std::vector<std::string>& extensions)
: GenericReaderPlugin(handle, extensions, kSupportsRGBA, kSupportsRGB, kSupportsAlpha, kSupportsTiles, false)
,_dpi(0)
{
    _dpi = fetchIntParam(kParamDpi);
    assert(_dpi);
}

ReadSVGPlugin::~ReadSVGPlugin()
{
}

void
ReadSVGPlugin::decode(const std::string& filename,
                      OfxTime time,
                      int /*view*/,
                      bool /*isPlayback*/,
                      const OfxRectI& renderWindow,
                      float *pixelData,
                      const OfxRectI& /*bounds*/,
                      OFX::PixelComponentEnum /*pixelComponents*/,
                      int pixelComponentCount,
                      int /*rowBytes*/)
{
    if (pixelComponentCount != 4) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Wrong pixel components");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    if (filename.empty()) {
        setPersistentMessage(OFX::Message::eMessageError, "", "No filename");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    GError *error = NULL;
    RsvgHandle *handle;
    RsvgDimensionData dimension;
    cairo_surface_t *surface;
    cairo_t *cr;
    cairo_status_t status;
    double imageWidth, imageHeight, scaleWidth, scaleHeight;
    int dpi, width, height, renderWidth, renderHeight;
    _dpi->getValueAtTime(time, dpi);

    rsvg_set_default_dpi_x_y(dpi, dpi);
    handle=rsvg_handle_new_from_file(filename.c_str(), &error);

    if (error != NULL) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Failed to read SVG");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    rsvg_handle_get_dimensions(handle, &dimension);

    imageWidth = dimension.width;
    imageHeight = dimension.height;
    renderWidth= renderWindow.x2 - renderWindow.x1;
    renderHeight= renderWindow.y2 - renderWindow.y1;

    if (dpi != kParamDpiDefault) {
        width = imageWidth * dpi / kParamDpiDefault;
        height = imageHeight * dpi / kParamDpiDefault;
    }
    else {
        width = imageWidth;
        height = imageHeight;
    }

    if (width != renderWidth || height != renderHeight) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Image don't match RenderWindow");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    surface=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
    cr=cairo_create(surface);

    scaleWidth = width / imageWidth;
    scaleHeight = height / imageHeight;

    cairo_scale(cr, scaleWidth, scaleHeight);

    rsvg_handle_render_cairo(handle, cr);
    status = cairo_status(cr);

    if (status) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Render failed");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    cairo_surface_flush(surface);

    unsigned char* cdata = cairo_image_surface_get_data(surface);
    unsigned char* pixels = new unsigned char[width * height * pixelComponentCount];
    for (int i = 0; i < width; ++i) {
        for (int j = 0; j < height; ++j) {
            for (int k = 0; k < pixelComponentCount; ++k)
                pixels[(i + j * width) * pixelComponentCount + k] = cdata[(i + (height - 1 - j) * width) * pixelComponentCount + k];
        }
    }

    int offset = 0;
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            pixelData[offset] = (float)pixels[offset + 2] / 255.f;
            pixelData[offset + 1] = (float)pixels[offset + 1] / 255.f;
            pixelData[offset + 2] = (float)pixels[offset] / 255.f;
            pixelData[offset + 3] = (float)pixels[offset + 3] / 255.f;
            offset += pixelComponentCount;
        }
    }

    g_object_unref(handle);
    cairo_destroy(cr);
    cairo_surface_destroy(surface);
    cdata = NULL;
    error = NULL;
    delete[] pixels;
}

bool ReadSVGPlugin::getFrameBounds(const std::string& filename,
                              OfxTime time,
                              OfxRectI *bounds,
                              double *par,
                              std::string* /*error*/,int *tile_width, int *tile_height)
{
    if (filename.empty()) {
        setPersistentMessage(OFX::Message::eMessageError, "", "No filename");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    int dpi;
    _dpi->getValueAtTime(time, dpi);

    GError *error = NULL;
    RsvgHandle *handle;
    RsvgDimensionData dimension;
    double imageWidth, imageHeight;
    int width, height;

    rsvg_set_default_dpi_x_y(dpi, dpi);

    handle=rsvg_handle_new_from_file(filename.c_str(), &error);

    if (error != NULL) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Failed to read SVG");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    rsvg_handle_get_dimensions(handle, &dimension);

    imageWidth = dimension.width;
    imageHeight = dimension.height;

    if (dpi != kParamDpiDefault) {
        width = imageWidth * dpi / kParamDpiDefault;
        height = imageHeight * dpi / kParamDpiDefault;
    }
    else {
        width = imageWidth;
        height = imageHeight;
    }

    g_object_unref(handle);
    error = NULL;

    if (width > 0 && height > 0) {
        bounds->x1 = 0;
        bounds->x2 = width;
        bounds->y1 = 0;
        bounds->y2 = height;
        *par = 1.0;
    }

    *tile_width = *tile_height = 0;
    return true;
}

void ReadSVGPlugin::onInputFileChanged(const std::string& newFile,
                                  bool setColorSpace,
                                  OFX::PreMultiplicationEnum *premult,
                                  OFX::PixelComponentEnum *components,int */*componentCount*/)
{
    assert(premult && components);

    if (newFile.empty()) {
        setPersistentMessage(OFX::Message::eMessageError, "", "No filename");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    GError *error = NULL;
    RsvgHandle *handle;

    handle=rsvg_handle_new_from_file(newFile.c_str(), &error);

    if (error != NULL) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Failed to read SVG");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    g_object_unref(handle);
    error = NULL;

    if (setColorSpace) {
# ifdef OFX_IO_USING_OCIO
        _ocio->setInputColorspace("sRGB");
# endif
    }

    *components = OFX::ePixelComponentRGBA;
    *premult = OFX::eImagePreMultiplied;
}

using namespace OFX;

mDeclareReaderPluginFactory(ReadSVGPluginFactory, {}, false);

void
ReadSVGPluginFactory::load()
{
    _extensions.clear();
    _extensions.push_back("svg");
    _extensions.push_back("svgz");
}

/** @brief The basic describe function, passed a plugin descriptor */
void ReadSVGPluginFactory::describe(OFX::ImageEffectDescriptor &desc)
{
    GenericReaderDescribe(desc, _extensions, kPluginEvaluation, kSupportsTiles, false);
    desc.setLabel(kPluginName);
    desc.setPluginDescription("Fast SVG (Scalable Vector Graphics) reader using librsvg and Cairo.");
}

/** @brief The describe in context function, passed a plugin descriptor and a context */
void ReadSVGPluginFactory::describeInContext(OFX::ImageEffectDescriptor &desc, ContextEnum context)
{
    PageParamDescriptor *page = GenericReaderDescribeInContextBegin(desc, context, isVideoStreamPlugin(), kSupportsRGBA, kSupportsRGB, kSupportsAlpha, kSupportsTiles, false);
    {
        IntParamDescriptor* param = desc.defineIntParam(kParamDpi);
        param->setLabel(kParamDpiLabel);
        param->setHint(kParamDpiHint);
        param->setRange(1, 10000);
        param->setDisplayRange(1, 500);
        param->setDefault(kParamDpiDefault);
        param->setAnimates(false);
        param->setLayoutHint(OFX::eLayoutHintDivider);
        page->addChild(*param);
    }

    GenericReaderDescribeInContextEnd(desc, context, page, "reference", "reference");
}

/** @brief The create instance function, the plugin must return an object derived from the \ref OFX::ImageEffect class */
ImageEffect* ReadSVGPluginFactory::createInstance(OfxImageEffectHandle handle,
                                     ContextEnum /*context*/)
{
    ReadSVGPlugin* ret =  new ReadSVGPlugin(handle, _extensions);
    ret->restoreStateFromParameters();
    return ret;
}

static ReadSVGPluginFactory p(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
mRegisterPluginFactoryInstance(p)
<commit_msg>ReadSVG: support multiplane (layers), #222<commit_after>/*
 * This file is part of openfx-arena <https://github.com/olear/openfx-arena>,
 * Copyright (C) 2015, 2016 FxArena DA
 * Copyright (C) 2016 INRIA
 *
 * openfx-arena is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 *
 * openfx-arena 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 openfx-arena.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
*/

#include <librsvg/rsvg.h>

#ifdef LEGACY
#include "librsvg/rsvg-cairo.h"
#endif

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#include <iostream>

#include "ofxNatron.h"
#include "GenericReader.h"
#include "GenericOCIO.h"
#include "ofxsMacros.h"
#include "ofxsImageEffect.h"

#ifdef OFX_IO_USING_OCIO
#include <OpenColorIO/OpenColorIO.h>
#endif

#define kPluginName "ReadSVG"
#define kPluginGrouping "Image/Readers"
#define kPluginIdentifier "net.fxarena.openfx.ReadSVG"
#define kPluginVersionMajor 3
#define kPluginVersionMinor 0
#define kPluginEvaluation 50

#define kParamDpi "dpi"
#define kParamDpiLabel "DPI"
#define kParamDpiHint "Dots-per-inch (90 is default)"
#define kParamDpiDefault 90

#define kSupportsRGBA true
#define kSupportsRGB false
#define kSupportsAlpha false
#define kSupportsTiles false
#define kIsMultiPlanar true

static bool gHostIsNatron = false;

class ReadSVGPlugin : public GenericReaderPlugin
{
public:
    ReadSVGPlugin(OfxImageEffectHandle handle, const std::vector<std::string>& extensions);
    virtual ~ReadSVGPlugin();
private:
    virtual bool isVideoStream(const std::string& /*filename*/) OVERRIDE FINAL { return false; }
    virtual void decode(const std::string& filename, OfxTime time, int view, bool isPlayback, const OfxRectI& renderWindow, float *pixelData, const OfxRectI& bounds,
                             OFX::PixelComponentEnum pixelComponents, int pixelComponentCount, int rowBytes) OVERRIDE FINAL
    {
        std::string rawComps;
        switch (pixelComponents) {
            case OFX::ePixelComponentAlpha:
                rawComps = kOfxImageComponentAlpha;
                break;
            case OFX::ePixelComponentRGB:
                rawComps = kOfxImageComponentRGB;
                break;
            case OFX::ePixelComponentRGBA:
                rawComps = kOfxImageComponentRGBA;
                break;
            default:
                OFX::throwSuiteStatusException(kOfxStatFailed);
                return;
        }
        decodePlane(filename, time, view, isPlayback, renderWindow, pixelData, bounds, pixelComponents, pixelComponentCount, rawComps, rowBytes);
    }
    virtual void decodePlane(const std::string& filename, OfxTime time, int view, bool isPlayback, const OfxRectI& renderWindow, float *pixelData, const OfxRectI& bounds, OFX::PixelComponentEnum pixelComponents, int pixelComponentCount, const std::string& rawComponents, int rowBytes) OVERRIDE FINAL;
    virtual void getClipComponents(const OFX::ClipComponentsArguments& args, OFX::ClipComponentsSetter& clipComponents) OVERRIDE FINAL;
    virtual bool getFrameBounds(const std::string& filename, OfxTime time, OfxRectI *bounds, double *par, std::string *error, int *tile_width, int *tile_height) OVERRIDE FINAL;
    virtual void restoreState(const std::string& filename) OVERRIDE FINAL;
    virtual void onInputFileChanged(const std::string& newFile, bool setColorSpace, OFX::PreMultiplicationEnum *premult, OFX::PixelComponentEnum *components, int *componentCount) OVERRIDE FINAL;
    void getLayers(xmlNode *node, std::vector<std::string> *layers);
    OFX::IntParam *_dpi;
    std::vector<std::string> imageLayers;
};

ReadSVGPlugin::ReadSVGPlugin(OfxImageEffectHandle handle, const std::vector<std::string>& extensions)
: GenericReaderPlugin(handle, extensions, kSupportsRGBA, kSupportsRGB, kSupportsAlpha, kSupportsTiles,
#ifdef OFX_EXTENSIONS_NUKE
(OFX::getImageEffectHostDescription() && OFX::getImageEffectHostDescription()->isMultiPlanar) ? kIsMultiPlanar : false
#else
false
#endif
)
,_dpi(0)
{
    _dpi = fetchIntParam(kParamDpi);
    assert(_dpi);
}

ReadSVGPlugin::~ReadSVGPlugin()
{
}

void
ReadSVGPlugin::getLayers(xmlNode *node, std::vector<std::string> *layers)
{
    xmlNode *cur_node = NULL;
    for (cur_node = node; cur_node; cur_node = cur_node->next) {
        if (cur_node->type == XML_ELEMENT_NODE) {
            if ((!xmlStrcmp(cur_node->name, (const xmlChar *)"g"))) {
                xmlChar *xmlID;
                xmlID = xmlGetProp(cur_node, (const xmlChar *)"id");
                if (xmlID!=NULL) {
                    std::string layerName;
                    layerName = (reinterpret_cast<char*>(xmlID));
                    layers->push_back(layerName);
                }
                xmlFree(xmlID);
            }
        }
        getLayers(cur_node->children,layers);
    }
}

void
ReadSVGPlugin::getClipComponents(const OFX::ClipComponentsArguments& args, OFX::ClipComponentsSetter& clipComponents)
{
    assert(isMultiPlanar());
    clipComponents.addClipComponents(*_outputClip, getOutputComponents());
    clipComponents.setPassThroughClip(NULL, args.time, args.view);
    if (imageLayers.size()>0 && gHostIsNatron) {
        for (int i = 0; i < (int)imageLayers.size(); i++) {
            if (!imageLayers[i].empty()) {
                std::string component(kNatronOfxImageComponentsPlane);
                component.append(imageLayers[i]);
                component.append(kNatronOfxImageComponentsPlaneChannel);
                component.append("R");
                component.append(kNatronOfxImageComponentsPlaneChannel);
                component.append("G");
                component.append(kNatronOfxImageComponentsPlaneChannel);
                component.append("B");
                component.append(kNatronOfxImageComponentsPlaneChannel);
                component.append("A");
                clipComponents.addClipComponents(*_outputClip, component);
            }
        }
    }
}

void
ReadSVGPlugin::decodePlane(const std::string& filename, OfxTime time, int /*view*/, bool /*isPlayback*/, const OfxRectI& renderWindow, float *pixelData, const OfxRectI& /*bounds*/,
                                 OFX::PixelComponentEnum /*pixelComponents*/, int pixelComponentCount, const std::string& rawComponents, int /*rowBytes*/)
{
    if (pixelComponentCount != 4) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Wrong pixel components");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    if (filename.empty()) {
        setPersistentMessage(OFX::Message::eMessageError, "", "No filename");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    std::string layerID;
    if (gHostIsNatron) {
        std::string layerName;
        std::vector<std::string> layerChannels = OFX::mapPixelComponentCustomToLayerChannels(rawComponents);
        int numChannels = layerChannels.size();
        if (numChannels==5) // layer+R+G+B+A
            layerName=layerChannels[0];
        if (!layerName.empty()) {
            layerID = layerName;
        }
    }

    GError *error = NULL;
    RsvgHandle *handle;
    RsvgDimensionData dimension;
    cairo_surface_t *surface;
    cairo_t *cr;
    cairo_status_t status;
    double imageWidth, imageHeight, scaleWidth, scaleHeight;
    int dpi, width, height, renderWidth, renderHeight;
    _dpi->getValueAtTime(time, dpi);

    rsvg_set_default_dpi_x_y(dpi, dpi);
    handle=rsvg_handle_new_from_file(filename.c_str(), &error);

    if (error != NULL) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Failed to read SVG");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    rsvg_handle_get_dimensions(handle, &dimension);

    imageWidth = dimension.width;
    imageHeight = dimension.height;
    renderWidth= renderWindow.x2 - renderWindow.x1;
    renderHeight= renderWindow.y2 - renderWindow.y1;

    if (dpi != kParamDpiDefault) {
        width = imageWidth * dpi / kParamDpiDefault;
        height = imageHeight * dpi / kParamDpiDefault;
    }
    else {
        width = imageWidth;
        height = imageHeight;
    }

    if (width != renderWidth || height != renderHeight) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Image don't match RenderWindow");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    surface=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
    cr=cairo_create(surface);

    scaleWidth = width / imageWidth;
    scaleHeight = height / imageHeight;

    cairo_scale(cr, scaleWidth, scaleHeight);

    if (layerID.empty()) {
        rsvg_handle_render_cairo(handle, cr);
    }
    else {
        std::ostringstream layerSub;
        layerSub << "#" << layerID;
        rsvg_handle_render_cairo_sub(handle, cr, layerSub.str().c_str());
    }

    status = cairo_status(cr);

    if (status) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Cairo Render failed");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    cairo_surface_flush(surface);

    unsigned char* cdata = cairo_image_surface_get_data(surface);
    unsigned char* pixels = new unsigned char[width * height * pixelComponentCount];
    for (int i = 0; i < width; ++i) {
        for (int j = 0; j < height; ++j) {
            for (int k = 0; k < pixelComponentCount; ++k)
                pixels[(i + j * width) * pixelComponentCount + k] = cdata[(i + (height - 1 - j) * width) * pixelComponentCount + k];
        }
    }

    int offset = 0;
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            pixelData[offset] = (float)pixels[offset + 2] / 255.f;
            pixelData[offset + 1] = (float)pixels[offset + 1] / 255.f;
            pixelData[offset + 2] = (float)pixels[offset] / 255.f;
            pixelData[offset + 3] = (float)pixels[offset + 3] / 255.f;
            offset += pixelComponentCount;
        }
    }

    g_object_unref(handle);
    cairo_destroy(cr);
    cairo_surface_destroy(surface);
    cdata = NULL;
    error = NULL;
    delete[] pixels;
}

bool ReadSVGPlugin::getFrameBounds(const std::string& filename,
                              OfxTime time,
                              OfxRectI *bounds,
                              double *par,
                              std::string* /*error*/,int *tile_width, int *tile_height)
{
    if (filename.empty()) {
        setPersistentMessage(OFX::Message::eMessageError, "", "No filename");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    int dpi;
    _dpi->getValueAtTime(time, dpi);

    GError *error = NULL;
    RsvgHandle *handle;
    RsvgDimensionData dimension;
    double imageWidth, imageHeight;
    int width, height;

    rsvg_set_default_dpi_x_y(dpi, dpi);

    handle=rsvg_handle_new_from_file(filename.c_str(), &error);

    if (error != NULL) {
        setPersistentMessage(OFX::Message::eMessageError, "", "Failed to read SVG");
        OFX::throwSuiteStatusException(kOfxStatErrFormat);
    }

    rsvg_handle_get_dimensions(handle, &dimension);

    imageWidth = dimension.width;
    imageHeight = dimension.height;

    if (dpi != kParamDpiDefault) {
        width = imageWidth * dpi / kParamDpiDefault;
        height = imageHeight * dpi / kParamDpiDefault;
    }
    else {
        width = imageWidth;
        height = imageHeight;
    }

    g_object_unref(handle);
    error = NULL;

    if (width > 0 && height > 0) {
        bounds->x1 = 0;
        bounds->x2 = width;
        bounds->y1 = 0;
        bounds->y2 = height;
        *par = 1.0;
    }

    *tile_width = *tile_height = 0;
    return true;
}

void
ReadSVGPlugin::restoreState(const std::string& filename)
{
    if (!filename.empty()) {
        imageLayers.clear();
        xmlDocPtr doc;
        doc = xmlParseFile(filename.c_str());
        xmlNode *root_element = NULL;
        root_element = xmlDocGetRootElement(doc);
        getLayers(root_element,&imageLayers);
        xmlFreeDoc(doc);

        GError *error = NULL;
        RsvgHandle *handle;

        handle=rsvg_handle_new_from_file(filename.c_str(), &error);

        if (error != NULL) {
            setPersistentMessage(OFX::Message::eMessageError, "", "Failed to read SVG");
        }

        g_object_unref(handle);
        error = NULL;
    }
    else {
        setPersistentMessage(OFX::Message::eMessageError, "", "Empty and/or corrupt image");
    }
}

void ReadSVGPlugin::onInputFileChanged(const std::string& newFile,
                                  bool setColorSpace,
                                  OFX::PreMultiplicationEnum *premult,
                                  OFX::PixelComponentEnum *components,int */*componentCount*/)
{
    assert(premult && components);
    restoreState(newFile);
    if (setColorSpace) {
# ifdef OFX_IO_USING_OCIO
        _ocio->setInputColorspace("sRGB");
# endif
    }

    *components = OFX::ePixelComponentRGBA;
    *premult = OFX::eImageUnPreMultiplied;
}

using namespace OFX;

mDeclareReaderPluginFactory(ReadSVGPluginFactory, {}, false);

void
ReadSVGPluginFactory::load()
{
    _extensions.clear();
    _extensions.push_back("svg");
    _extensions.push_back("svgz");
}

/** @brief The basic describe function, passed a plugin descriptor */
void ReadSVGPluginFactory::describe(OFX::ImageEffectDescriptor &desc)
{
    GenericReaderDescribe(desc, _extensions, kPluginEvaluation, kSupportsTiles, kIsMultiPlanar);
    desc.setLabel(kPluginName);
    desc.setPluginDescription("Fast SVG (Scalable Vector Graphics) reader using librsvg and Cairo.");
}

/** @brief The describe in context function, passed a plugin descriptor and a context */
void ReadSVGPluginFactory::describeInContext(OFX::ImageEffectDescriptor &desc, ContextEnum context)
{
    gHostIsNatron = (OFX::getImageEffectHostDescription()->isNatron);

    PageParamDescriptor *page = GenericReaderDescribeInContextBegin(desc, context, isVideoStreamPlugin(), kSupportsRGBA, kSupportsRGB, kSupportsAlpha, kSupportsTiles, false);
    {
        IntParamDescriptor* param = desc.defineIntParam(kParamDpi);
        param->setLabel(kParamDpiLabel);
        param->setHint(kParamDpiHint);
        param->setRange(1, 10000);
        param->setDisplayRange(1, 500);
        param->setDefault(kParamDpiDefault);
        param->setAnimates(false);
        param->setLayoutHint(OFX::eLayoutHintDivider);
        page->addChild(*param);
    }

    GenericReaderDescribeInContextEnd(desc, context, page, "reference", "reference");
}

/** @brief The create instance function, the plugin must return an object derived from the \ref OFX::ImageEffect class */
ImageEffect* ReadSVGPluginFactory::createInstance(OfxImageEffectHandle handle,
                                     ContextEnum /*context*/)
{
    ReadSVGPlugin* ret =  new ReadSVGPlugin(handle, _extensions);
    ret->restoreStateFromParameters();
    return ret;
}

static ReadSVGPluginFactory p(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
mRegisterPluginFactoryInstance(p)
<|endoftext|>


<commit_before>// Copyright 2019 DeepMind Technologies Limited.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "reverb/cc/client.h"

#include <memory>
#include <string>
#include <vector>

#include <cstdint>
#include "absl/strings/str_cat.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/resource_op_kernel.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/lib/core/status.h"

namespace deepmind {
namespace reverb {
namespace {

using ::tensorflow::tstring;
using ::tensorflow::errors::InvalidArgument;

REGISTER_OP("ReverbClient")
    .Output("handle: resource")
    .Attr("server_address: string")
    .Attr("container: string = ''")
    .Attr("shared_name: string = ''")
    .SetIsStateful()
    .SetShapeFn(tensorflow::shape_inference::ScalarShape)
    .Doc(R"doc(
Constructs a `ClientResource` that constructs a `Client` connected to
`server_address`. The resource allows ops to share the stub across calls.
)doc");

REGISTER_OP("ReverbClientSample")
    .Attr("Toutput_list: list(type) >= 0")
    .Input("handle: resource")
    .Input("table: string")
    .Output("key: uint64")
    .Output("probability: double")
    .Output("table_size: int64")
    .Output("priority: double")
    .Output("outputs: Toutput_list")
    .Doc(R"doc(
Blocking call to sample a single item from table `table` using shared resource.
A `SampleStream`-stream is opened  between the client and the server and when
the one sample has been received, the stream is closed.

Prefer to use `ReverbDataset` when requesting more than one sample to avoid
opening and closing the stream with each call.
)doc");

REGISTER_OP("ReverbClientUpdatePriorities")
    .Input("handle: resource")
    .Input("table: string")
    .Input("keys: uint64")
    .Input("priorities: double")
    .Doc(R"doc(
Blocking call to update the priorities of a collection of items. Keys that could
not be found in table `table` on server are ignored and does not impact the rest
of the request.
)doc");

REGISTER_OP("ReverbClientInsert")
    .Attr("T: list(type) >= 0")
    .Input("handle: resource")
    .Input("data: T")
    .Input("tables: string")
    .Input("priorities: double")
    .Doc(R"doc(
Blocking call to insert a single trajectory into one or more tables. The data
is treated as an episode constituting of a single timestep. Note that this mean
that when the item is sampled, it will be returned as a sequence of length 1,
containing `data`.
)doc");

class ClientResource : public tensorflow::ResourceBase {
 public:
  explicit ClientResource(const std::string& server_address)
      : tensorflow::ResourceBase(),
        client_(server_address),
        server_address_(server_address) {}

  std::string DebugString() const override {
    return tensorflow::strings::StrCat("Client with server address: ",
                                       server_address_);
  }

  Client* client() { return &client_; }

 private:
  Client client_;
  std::string server_address_;

  TF_DISALLOW_COPY_AND_ASSIGN(ClientResource);
};

class ClientHandleOp : public tensorflow::ResourceOpKernel<ClientResource> {
 public:
  explicit ClientHandleOp(tensorflow::OpKernelConstruction* context)
      : tensorflow::ResourceOpKernel<ClientResource>(context) {
    OP_REQUIRES_OK(context,
                   context->GetAttr("server_address", &server_address_));
  }

 private:
  tensorflow::Status CreateResource(ClientResource** ret) override {
    *ret = new ClientResource(server_address_);
    return tensorflow::Status::OK();
  }

  std::string server_address_;

  TF_DISALLOW_COPY_AND_ASSIGN(ClientHandleOp);
};

class SampleOp : public tensorflow::OpKernel {
 public:
  explicit SampleOp(tensorflow::OpKernelConstruction* context)
      : OpKernel(context) {}

  void Compute(tensorflow::OpKernelContext* context) override {
    ClientResource* resource;
    OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0),
                                           &resource));

    const tensorflow::Tensor* table_tensor;
    OP_REQUIRES_OK(context, context->input("table", &table_tensor));
    std::string table = table_tensor->scalar<tstring>()();

    std::vector<tensorflow::Tensor> sample;
    std::unique_ptr<Sampler> sampler;

    Sampler::Options options;
    options.max_samples = 1;
    options.max_in_flight_samples_per_worker = 1;

    constexpr auto kValidationTimeout = absl::Seconds(30);
    OP_REQUIRES_OK(
        context, resource->client()->NewSampler(
                     table, options, /*validation_timeout=*/kValidationTimeout,
                     &sampler));
    OP_REQUIRES_OK(context, sampler->GetNextTimestep(&sample, nullptr));
    OP_REQUIRES(context, sample.size() == context->num_outputs(),
                InvalidArgument(
                    "Number of tensors in the replay sample did not match the "
                    "expected count."));

    for (int i = 0; i < sample.size(); i++) {
      tensorflow::Tensor* tensor;
      OP_REQUIRES_OK(context,
                     context->allocate_output(i, sample[i].shape(), &tensor));
      *tensor = std::move(sample[i]);
    }
  }

  TF_DISALLOW_COPY_AND_ASSIGN(SampleOp);
};

class UpdatePrioritiesOp : public tensorflow::OpKernel {
 public:
  explicit UpdatePrioritiesOp(tensorflow::OpKernelConstruction* context)
      : OpKernel(context) {}

  void Compute(tensorflow::OpKernelContext* context) override {
    ClientResource* resource;
    OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0),
                                           &resource));

    const tensorflow::Tensor* table;
    OP_REQUIRES_OK(context, context->input("table", &table));
    const tensorflow::Tensor* keys;
    OP_REQUIRES_OK(context, context->input("keys", &keys));
    const tensorflow::Tensor* priorities;
    OP_REQUIRES_OK(context, context->input("priorities", &priorities));

    OP_REQUIRES(
        context, keys->dims() == 1,
        InvalidArgument("Tensors `keys` and `priorities` must be of rank 1."));
    OP_REQUIRES(context, keys->shape() == priorities->shape(),
                InvalidArgument(
                    "Tensors `keys` and `priorities` do not match in shape."));

    std::string table_str = table->scalar<tstring>()();
    std::vector<KeyWithPriority> updates;
    for (int i = 0; i < keys->dim_size(0); i++) {
      KeyWithPriority update;
      update.set_key(keys->flat<tensorflow::uint64>()(i));
      update.set_priority(priorities->flat<double>()(i));
      updates.push_back(std::move(update));
    }

    // The call will only fail if the Reverb-server is brought down during an
    // active call (e.g preempted). When this happens the request is retried and
    // since MutatePriorities sets `wait_for_ready` the request will no be sent
    // before the server is brought up again. It is therefore no problem to have
    // this retry in this tight loop.
    tensorflow::Status status;
    do {
      status = resource->client()->MutatePriorities(table_str, updates, {});
    } while (tensorflow::errors::IsUnavailable(status) ||
             tensorflow::errors::IsDeadlineExceeded(status));
    OP_REQUIRES_OK(context, status);
  }

  TF_DISALLOW_COPY_AND_ASSIGN(UpdatePrioritiesOp);
};

class InsertOp : public tensorflow::OpKernel {
 public:
  explicit InsertOp(tensorflow::OpKernelConstruction* context)
      : OpKernel(context) {}

  void Compute(tensorflow::OpKernelContext* context) override {
    ClientResource* resource;
    OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0),
                                           &resource));

    const tensorflow::Tensor* tables;
    OP_REQUIRES_OK(context, context->input("tables", &tables));
    const tensorflow::Tensor* priorities;
    OP_REQUIRES_OK(context, context->input("priorities", &priorities));

    OP_REQUIRES(context, tables->dims() == 1 && priorities->dims() == 1,
                InvalidArgument(
                    "Tensors `tables` and `priorities` must be of rank 1."));
    OP_REQUIRES(
        context, tables->shape() == priorities->shape(),
        InvalidArgument(
            "Tensors `tables` and `priorities` do not match in shape."));

    tensorflow::OpInputList data;
    OP_REQUIRES_OK(context, context->input_list("data", &data));

    std::vector<tensorflow::Tensor> tensors;
    for (const auto& i : data) {
      tensors.push_back(i);
    }

    std::unique_ptr<Writer> writer;
    OP_REQUIRES_OK(context,
                   resource->client()->NewWriter(1, 1, false, &writer));
    OP_REQUIRES_OK(context, writer->Append(std::move(tensors)));

    auto tables_t = tables->flat<tstring>();
    auto priorities_t = priorities->flat<double>();
    for (int i = 0; i < tables->dim_size(0); i++) {
      OP_REQUIRES_OK(context,
                     writer->CreateItem(tables_t(i), 1, priorities_t(i)));
    }

    OP_REQUIRES_OK(context, writer->Close());
  }

  TF_DISALLOW_COPY_AND_ASSIGN(InsertOp);
};

REGISTER_KERNEL_BUILDER(Name("ReverbClient").Device(tensorflow::DEVICE_CPU),
                        ClientHandleOp);

REGISTER_KERNEL_BUILDER(
    Name("ReverbClientInsert").Device(tensorflow::DEVICE_CPU), InsertOp);

REGISTER_KERNEL_BUILDER(
    Name("ReverbClientSample").Device(tensorflow::DEVICE_CPU), SampleOp);

REGISTER_KERNEL_BUILDER(
    Name("ReverbClientUpdatePriorities").Device(tensorflow::DEVICE_CPU),
    UpdatePrioritiesOp);

}  // namespace
}  // namespace reverb
}  // namespace deepmind
<commit_msg>Add shape information to error message when keys and priorities tensors don't match.<commit_after>// Copyright 2019 DeepMind Technologies Limited.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "reverb/cc/client.h"

#include <memory>
#include <string>
#include <vector>

#include <cstdint>
#include "absl/strings/str_cat.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/resource_op_kernel.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/lib/core/status.h"

namespace deepmind {
namespace reverb {
namespace {

using ::tensorflow::tstring;
using ::tensorflow::errors::InvalidArgument;

REGISTER_OP("ReverbClient")
    .Output("handle: resource")
    .Attr("server_address: string")
    .Attr("container: string = ''")
    .Attr("shared_name: string = ''")
    .SetIsStateful()
    .SetShapeFn(tensorflow::shape_inference::ScalarShape)
    .Doc(R"doc(
Constructs a `ClientResource` that constructs a `Client` connected to
`server_address`. The resource allows ops to share the stub across calls.
)doc");

REGISTER_OP("ReverbClientSample")
    .Attr("Toutput_list: list(type) >= 0")
    .Input("handle: resource")
    .Input("table: string")
    .Output("key: uint64")
    .Output("probability: double")
    .Output("table_size: int64")
    .Output("priority: double")
    .Output("outputs: Toutput_list")
    .Doc(R"doc(
Blocking call to sample a single item from table `table` using shared resource.
A `SampleStream`-stream is opened  between the client and the server and when
the one sample has been received, the stream is closed.

Prefer to use `ReverbDataset` when requesting more than one sample to avoid
opening and closing the stream with each call.
)doc");

REGISTER_OP("ReverbClientUpdatePriorities")
    .Input("handle: resource")
    .Input("table: string")
    .Input("keys: uint64")
    .Input("priorities: double")
    .Doc(R"doc(
Blocking call to update the priorities of a collection of items. Keys that could
not be found in table `table` on server are ignored and does not impact the rest
of the request.
)doc");

REGISTER_OP("ReverbClientInsert")
    .Attr("T: list(type) >= 0")
    .Input("handle: resource")
    .Input("data: T")
    .Input("tables: string")
    .Input("priorities: double")
    .Doc(R"doc(
Blocking call to insert a single trajectory into one or more tables. The data
is treated as an episode constituting of a single timestep. Note that this mean
that when the item is sampled, it will be returned as a sequence of length 1,
containing `data`.
)doc");

class ClientResource : public tensorflow::ResourceBase {
 public:
  explicit ClientResource(const std::string& server_address)
      : tensorflow::ResourceBase(),
        client_(server_address),
        server_address_(server_address) {}

  std::string DebugString() const override {
    return tensorflow::strings::StrCat("Client with server address: ",
                                       server_address_);
  }

  Client* client() { return &client_; }

 private:
  Client client_;
  std::string server_address_;

  TF_DISALLOW_COPY_AND_ASSIGN(ClientResource);
};

class ClientHandleOp : public tensorflow::ResourceOpKernel<ClientResource> {
 public:
  explicit ClientHandleOp(tensorflow::OpKernelConstruction* context)
      : tensorflow::ResourceOpKernel<ClientResource>(context) {
    OP_REQUIRES_OK(context,
                   context->GetAttr("server_address", &server_address_));
  }

 private:
  tensorflow::Status CreateResource(ClientResource** ret) override {
    *ret = new ClientResource(server_address_);
    return tensorflow::Status::OK();
  }

  std::string server_address_;

  TF_DISALLOW_COPY_AND_ASSIGN(ClientHandleOp);
};

class SampleOp : public tensorflow::OpKernel {
 public:
  explicit SampleOp(tensorflow::OpKernelConstruction* context)
      : OpKernel(context) {}

  void Compute(tensorflow::OpKernelContext* context) override {
    ClientResource* resource;
    OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0),
                                           &resource));

    const tensorflow::Tensor* table_tensor;
    OP_REQUIRES_OK(context, context->input("table", &table_tensor));
    std::string table = table_tensor->scalar<tstring>()();

    std::vector<tensorflow::Tensor> sample;
    std::unique_ptr<Sampler> sampler;

    Sampler::Options options;
    options.max_samples = 1;
    options.max_in_flight_samples_per_worker = 1;

    constexpr auto kValidationTimeout = absl::Seconds(30);
    OP_REQUIRES_OK(
        context, resource->client()->NewSampler(
                     table, options, /*validation_timeout=*/kValidationTimeout,
                     &sampler));
    OP_REQUIRES_OK(context, sampler->GetNextTimestep(&sample, nullptr));
    OP_REQUIRES(context, sample.size() == context->num_outputs(),
                InvalidArgument(
                    "Number of tensors in the replay sample did not match the "
                    "expected count."));

    for (int i = 0; i < sample.size(); i++) {
      tensorflow::Tensor* tensor;
      OP_REQUIRES_OK(context,
                     context->allocate_output(i, sample[i].shape(), &tensor));
      *tensor = std::move(sample[i]);
    }
  }

  TF_DISALLOW_COPY_AND_ASSIGN(SampleOp);
};

class UpdatePrioritiesOp : public tensorflow::OpKernel {
 public:
  explicit UpdatePrioritiesOp(tensorflow::OpKernelConstruction* context)
      : OpKernel(context) {}

  void Compute(tensorflow::OpKernelContext* context) override {
    ClientResource* resource;
    OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0),
                                           &resource));

    const tensorflow::Tensor* table;
    OP_REQUIRES_OK(context, context->input("table", &table));
    const tensorflow::Tensor* keys;
    OP_REQUIRES_OK(context, context->input("keys", &keys));
    const tensorflow::Tensor* priorities;
    OP_REQUIRES_OK(context, context->input("priorities", &priorities));

    OP_REQUIRES(
        context, keys->dims() == 1,
        InvalidArgument("Tensors `keys` and `priorities` must be of rank 1."));
    OP_REQUIRES(context, keys->shape() == priorities->shape(),
                InvalidArgument(
                    "Tensors `keys` and `priorities` do not match in shape (",
                    keys->shape().DebugString(), " vs. ",
                    priorities->shape().DebugString(), ")"));

    std::string table_str = table->scalar<tstring>()();
    std::vector<KeyWithPriority> updates;
    for (int i = 0; i < keys->dim_size(0); i++) {
      KeyWithPriority update;
      update.set_key(keys->flat<tensorflow::uint64>()(i));
      update.set_priority(priorities->flat<double>()(i));
      updates.push_back(std::move(update));
    }

    // The call will only fail if the Reverb-server is brought down during an
    // active call (e.g preempted). When this happens the request is retried and
    // since MutatePriorities sets `wait_for_ready` the request will no be sent
    // before the server is brought up again. It is therefore no problem to have
    // this retry in this tight loop.
    tensorflow::Status status;
    do {
      status = resource->client()->MutatePriorities(table_str, updates, {});
    } while (tensorflow::errors::IsUnavailable(status) ||
             tensorflow::errors::IsDeadlineExceeded(status));
    OP_REQUIRES_OK(context, status);
  }

  TF_DISALLOW_COPY_AND_ASSIGN(UpdatePrioritiesOp);
};

class InsertOp : public tensorflow::OpKernel {
 public:
  explicit InsertOp(tensorflow::OpKernelConstruction* context)
      : OpKernel(context) {}

  void Compute(tensorflow::OpKernelContext* context) override {
    ClientResource* resource;
    OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0),
                                           &resource));

    const tensorflow::Tensor* tables;
    OP_REQUIRES_OK(context, context->input("tables", &tables));
    const tensorflow::Tensor* priorities;
    OP_REQUIRES_OK(context, context->input("priorities", &priorities));

    OP_REQUIRES(context, tables->dims() == 1 && priorities->dims() == 1,
                InvalidArgument(
                    "Tensors `tables` and `priorities` must be of rank 1."));
    OP_REQUIRES(
        context, tables->shape() == priorities->shape(),
        InvalidArgument(
            "Tensors `tables` and `priorities` do not match in shape."));

    tensorflow::OpInputList data;
    OP_REQUIRES_OK(context, context->input_list("data", &data));

    std::vector<tensorflow::Tensor> tensors;
    for (const auto& i : data) {
      tensors.push_back(i);
    }

    std::unique_ptr<Writer> writer;
    OP_REQUIRES_OK(context,
                   resource->client()->NewWriter(1, 1, false, &writer));
    OP_REQUIRES_OK(context, writer->Append(std::move(tensors)));

    auto tables_t = tables->flat<tstring>();
    auto priorities_t = priorities->flat<double>();
    for (int i = 0; i < tables->dim_size(0); i++) {
      OP_REQUIRES_OK(context,
                     writer->CreateItem(tables_t(i), 1, priorities_t(i)));
    }

    OP_REQUIRES_OK(context, writer->Close());
  }

  TF_DISALLOW_COPY_AND_ASSIGN(InsertOp);
};

REGISTER_KERNEL_BUILDER(Name("ReverbClient").Device(tensorflow::DEVICE_CPU),
                        ClientHandleOp);

REGISTER_KERNEL_BUILDER(
    Name("ReverbClientInsert").Device(tensorflow::DEVICE_CPU), InsertOp);

REGISTER_KERNEL_BUILDER(
    Name("ReverbClientSample").Device(tensorflow::DEVICE_CPU), SampleOp);

REGISTER_KERNEL_BUILDER(
    Name("ReverbClientUpdatePriorities").Device(tensorflow::DEVICE_CPU),
    UpdatePrioritiesOp);

}  // namespace
}  // namespace reverb
}  // namespace deepmind
<|endoftext|>


<commit_before>/*=========================================================================

  Library:   CTK

  Copyright (c) Kitware Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=========================================================================*/

// Qt includes
#include <QDebug>
#include <QAbstractItemModel>
#include <QStandardItemModel>

// CTK includes
#include "ctkErrorLogWidget.h"
#include "ui_ctkErrorLogWidget.h"
#include <ctkErrorLogModel.h>

class ctkErrorLogWidgetPrivate : public Ui_ctkErrorLogWidget
{
  Q_DECLARE_PUBLIC(ctkErrorLogWidget);
protected:
  ctkErrorLogWidget* const q_ptr;
public:
  typedef ctkErrorLogWidgetPrivate Self;
  ctkErrorLogWidgetPrivate(ctkErrorLogWidget& object);

  ctkErrorLogLevel::LogLevels ErrorButtonFilter;
  ctkErrorLogLevel::LogLevels WarningButtonFilter;
  ctkErrorLogLevel::LogLevels InfoButtonFilter;

  void init();

  QSharedPointer<QItemSelectionModel> SelectionModel;
};

// --------------------------------------------------------------------------
// ctkErrorLogWidgetPrivate methods

// --------------------------------------------------------------------------
ctkErrorLogWidgetPrivate::ctkErrorLogWidgetPrivate(ctkErrorLogWidget& object)
  : q_ptr(&object)
{
  this->ErrorButtonFilter = ctkErrorLogLevel::Error | ctkErrorLogLevel::Critical | ctkErrorLogLevel::Fatal;
  this->WarningButtonFilter = ctkErrorLogLevel::Warning;
  this->InfoButtonFilter = ctkErrorLogLevel::Info | ctkErrorLogLevel::Debug | ctkErrorLogLevel::Trace | ctkErrorLogLevel::Status;
}

// --------------------------------------------------------------------------
void ctkErrorLogWidgetPrivate::init()
{
  Q_Q(ctkErrorLogWidget);

  // this->ShowAllEntryButton->setIcon();
  this->ShowErrorEntryButton->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxCritical));
  this->ShowWarningEntryButton->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxWarning));
  this->ShowInfoEntryButton->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxInformation));
  this->ClearButton->setIcon(q->style()->standardIcon(QStyle::SP_DialogDiscardButton));

  QObject::connect(this->ShowAllEntryButton, SIGNAL(clicked()),
                   q, SLOT(setAllEntriesVisible()));

  QObject::connect(this->ShowErrorEntryButton, SIGNAL(clicked(bool)),
                   q, SLOT(setErrorEntriesVisible(bool)));

  QObject::connect(this->ShowWarningEntryButton, SIGNAL(clicked(bool)),
                   q, SLOT(setWarningEntriesVisible(bool)));

  QObject::connect(this->ShowInfoEntryButton, SIGNAL(clicked(bool)),
                   q, SLOT(setInfoEntriesVisible(bool)));

  QObject::connect(this->ClearButton, SIGNAL(clicked()),
                   q, SLOT(removeEntries()));
}

// --------------------------------------------------------------------------
// ctkErrorLogWidget methods

//------------------------------------------------------------------------------
ctkErrorLogWidget::ctkErrorLogWidget(QWidget * newParent)
  : Superclass(newParent)
  , d_ptr(new ctkErrorLogWidgetPrivate(*this))
{
  Q_D(ctkErrorLogWidget);

  d->setupUi(this);
  d->init();

  this->setErrorLogModel(new ctkErrorLogModel(d->ErrorLogTableView));
}

//------------------------------------------------------------------------------
ctkErrorLogWidget::~ctkErrorLogWidget()
{
}

//------------------------------------------------------------------------------
ctkErrorLogAbstractModel* ctkErrorLogWidget::errorLogModel()const
{
  Q_D(const ctkErrorLogWidget);
  QAbstractItemModel* model = d->ErrorLogTableView->model();
  ctkErrorLogAbstractModel * errorLogModel = qobject_cast<ctkErrorLogAbstractModel*>(model);
  Q_ASSERT(model ? errorLogModel != 0 : true);
  return errorLogModel;
}

//------------------------------------------------------------------------------
void ctkErrorLogWidget::setErrorLogModel(ctkErrorLogAbstractModel * newErrorLogModel)
{
  Q_D(ctkErrorLogWidget);

  if (newErrorLogModel == this->errorLogModel())
    {
    return;
    }

  if (this->errorLogModel())
    {
    disconnect(this->errorLogModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
               this, SLOT(onRowsInserted(QModelIndex,int,int)));

    disconnect(this->errorLogModel(), SIGNAL(logLevelFilterChanged()),
               this, SLOT(onLogLevelFilterChanged()));

    disconnect(d->SelectionModel.data(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
               this, SLOT(onSelectionChanged(QItemSelection,QItemSelection)));

    d->SelectionModel.clear();
    }

  d->ErrorLogTableView->setModel(newErrorLogModel);

  if (newErrorLogModel)
    {
    connect(this->errorLogModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
            this, SLOT(onRowsInserted(QModelIndex,int,int)));

    connect(this->errorLogModel(), SIGNAL(logLevelFilterChanged()),
            this, SLOT(onLogLevelFilterChanged()));

    ctkErrorLogLevel::LogLevels logLevelFilter = newErrorLogModel->logLevelFilter();
    this->setErrorEntriesVisible(logLevelFilter & d->ErrorButtonFilter);
    this->setWarningEntriesVisible(logLevelFilter & d->WarningButtonFilter);
    this->setInfoEntriesVisible(logLevelFilter & d->InfoButtonFilter);
    this->errorLogModel()->filterEntry(logLevelFilter & ctkErrorLogLevel::Unknown);

    // Setup selection model
    d->SelectionModel = QSharedPointer<QItemSelectionModel>(new QItemSelectionModel(this->errorLogModel()));
    d->SelectionModel->reset();

    connect(d->SelectionModel.data(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
            this, SLOT(onSelectionChanged(QItemSelection,QItemSelection)));

    d->ErrorLogTableView->setSelectionModel(d->SelectionModel.data());

    // Resize time column only if there are rows
    if (this->errorLogModel()->rowCount() > 0)
      {
      d->ErrorLogTableView->resizeColumnToContents(ctkErrorLogModel::TimeColumn);
      }
    }
  else
    {
    this->setAllEntriesVisible(0);
    }

  d->ErrorLogTableView->setColumnHidden(ctkErrorLogModel::ThreadIdColumn, true);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setColumnHidden(int columnId, bool hidden) const
{
  Q_D(const ctkErrorLogWidget);
  d->ErrorLogTableView->setColumnHidden(columnId, hidden);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setAllEntriesVisible(bool visibility)
{
  this->setErrorEntriesVisible(visibility);
  this->setWarningEntriesVisible(visibility);
  this->setInfoEntriesVisible(visibility);
  this->setUnknownEntriesVisible(visibility);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setErrorEntriesVisible(bool visibility)
{
  Q_D(ctkErrorLogWidget);
  if (!this->errorLogModel())
    {
    return;
    }
  this->errorLogModel()->filterEntry(d->ErrorButtonFilter, /* disableFilter= */ !visibility);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setWarningEntriesVisible(bool visibility)
{
  Q_D(ctkErrorLogWidget);
  if (!this->errorLogModel())
    {
    return;
    }
  this->errorLogModel()->filterEntry(d->WarningButtonFilter, /* disableFilter= */ !visibility);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setInfoEntriesVisible(bool visibility)
{
  Q_D(ctkErrorLogWidget);
  if (!this->errorLogModel())
    {
    return;
    }
  this->errorLogModel()->filterEntry(d->InfoButtonFilter, /* disableFilter= */ !visibility);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setUnknownEntriesVisible(bool visibility)
{
  if (!this->errorLogModel())
    {
    return;
    }
  this->errorLogModel()->filterEntry(ctkErrorLogLevel::Unknown,
      /* disableFilter= */ !visibility);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::onRowsInserted(const QModelIndex &/*parent*/, int /*first*/, int /*last*/)
{
  Q_D(ctkErrorLogWidget);
  if (d->ErrorLogTableView->model()->rowCount() == 1)
    {
    // For performance reason, resize first column only when first entry is added
    d->ErrorLogTableView->resizeColumnToContents(ctkErrorLogModel::TimeColumn);
    }
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::onLogLevelFilterChanged()
{
  Q_D(ctkErrorLogWidget);
  Q_ASSERT(this->errorLogModel());
  ctkErrorLogLevel::LogLevels logLevelFilter = this->errorLogModel()->logLevelFilter();
  d->ShowErrorEntryButton->setChecked(logLevelFilter & d->ErrorButtonFilter);
  d->ShowWarningEntryButton->setChecked(logLevelFilter & d->WarningButtonFilter);
  d->ShowInfoEntryButton->setChecked(logLevelFilter & d->InfoButtonFilter);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::removeEntries()
{
  Q_ASSERT(this->errorLogModel());
  this->errorLogModel()->clear();
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::onSelectionChanged(const QItemSelection & selected,
                                           const QItemSelection & deselected)
{
  // QTime start = QTime::currentTime();

  Q_D(ctkErrorLogWidget);
  Q_UNUSED(selected);
  Q_UNUSED(deselected);

  QModelIndexList selectedRows =
      d->SelectionModel->selectedRows(ctkErrorLogModel::DescriptionColumn);

  qSort(selectedRows.begin(), selectedRows.end());

  QStringList descriptions;

  foreach(const QModelIndex& index, selectedRows)
    {
    descriptions << index.data(ctkErrorLogModel::DescriptionTextRole).toString();
    }

  d->ErrorLogDescription->setText(descriptions.join("\n"));

  // fprintf(stdout, "onSelectionChanged: %d\n", start.msecsTo(QTime::currentTime()));
}
<commit_msg>COMP: Fix qSort deprecation warning<commit_after>/*=========================================================================

  Library:   CTK

  Copyright (c) Kitware Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=========================================================================*/

// Qt includes
#include <QDebug>
#include <QAbstractItemModel>
#include <QStandardItemModel>

// CTK includes
#include "ctkErrorLogWidget.h"
#include "ui_ctkErrorLogWidget.h"
#include <ctkErrorLogModel.h>

class ctkErrorLogWidgetPrivate : public Ui_ctkErrorLogWidget
{
  Q_DECLARE_PUBLIC(ctkErrorLogWidget);
protected:
  ctkErrorLogWidget* const q_ptr;
public:
  typedef ctkErrorLogWidgetPrivate Self;
  ctkErrorLogWidgetPrivate(ctkErrorLogWidget& object);

  ctkErrorLogLevel::LogLevels ErrorButtonFilter;
  ctkErrorLogLevel::LogLevels WarningButtonFilter;
  ctkErrorLogLevel::LogLevels InfoButtonFilter;

  void init();

  QSharedPointer<QItemSelectionModel> SelectionModel;
};

// --------------------------------------------------------------------------
// ctkErrorLogWidgetPrivate methods

// --------------------------------------------------------------------------
ctkErrorLogWidgetPrivate::ctkErrorLogWidgetPrivate(ctkErrorLogWidget& object)
  : q_ptr(&object)
{
  this->ErrorButtonFilter = ctkErrorLogLevel::Error | ctkErrorLogLevel::Critical | ctkErrorLogLevel::Fatal;
  this->WarningButtonFilter = ctkErrorLogLevel::Warning;
  this->InfoButtonFilter = ctkErrorLogLevel::Info | ctkErrorLogLevel::Debug | ctkErrorLogLevel::Trace | ctkErrorLogLevel::Status;
}

// --------------------------------------------------------------------------
void ctkErrorLogWidgetPrivate::init()
{
  Q_Q(ctkErrorLogWidget);

  // this->ShowAllEntryButton->setIcon();
  this->ShowErrorEntryButton->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxCritical));
  this->ShowWarningEntryButton->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxWarning));
  this->ShowInfoEntryButton->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxInformation));
  this->ClearButton->setIcon(q->style()->standardIcon(QStyle::SP_DialogDiscardButton));

  QObject::connect(this->ShowAllEntryButton, SIGNAL(clicked()),
                   q, SLOT(setAllEntriesVisible()));

  QObject::connect(this->ShowErrorEntryButton, SIGNAL(clicked(bool)),
                   q, SLOT(setErrorEntriesVisible(bool)));

  QObject::connect(this->ShowWarningEntryButton, SIGNAL(clicked(bool)),
                   q, SLOT(setWarningEntriesVisible(bool)));

  QObject::connect(this->ShowInfoEntryButton, SIGNAL(clicked(bool)),
                   q, SLOT(setInfoEntriesVisible(bool)));

  QObject::connect(this->ClearButton, SIGNAL(clicked()),
                   q, SLOT(removeEntries()));
}

// --------------------------------------------------------------------------
// ctkErrorLogWidget methods

//------------------------------------------------------------------------------
ctkErrorLogWidget::ctkErrorLogWidget(QWidget * newParent)
  : Superclass(newParent)
  , d_ptr(new ctkErrorLogWidgetPrivate(*this))
{
  Q_D(ctkErrorLogWidget);

  d->setupUi(this);
  d->init();

  this->setErrorLogModel(new ctkErrorLogModel(d->ErrorLogTableView));
}

//------------------------------------------------------------------------------
ctkErrorLogWidget::~ctkErrorLogWidget()
{
}

//------------------------------------------------------------------------------
ctkErrorLogAbstractModel* ctkErrorLogWidget::errorLogModel()const
{
  Q_D(const ctkErrorLogWidget);
  QAbstractItemModel* model = d->ErrorLogTableView->model();
  ctkErrorLogAbstractModel * errorLogModel = qobject_cast<ctkErrorLogAbstractModel*>(model);
  Q_ASSERT(model ? errorLogModel != 0 : true);
  return errorLogModel;
}

//------------------------------------------------------------------------------
void ctkErrorLogWidget::setErrorLogModel(ctkErrorLogAbstractModel * newErrorLogModel)
{
  Q_D(ctkErrorLogWidget);

  if (newErrorLogModel == this->errorLogModel())
    {
    return;
    }

  if (this->errorLogModel())
    {
    disconnect(this->errorLogModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
               this, SLOT(onRowsInserted(QModelIndex,int,int)));

    disconnect(this->errorLogModel(), SIGNAL(logLevelFilterChanged()),
               this, SLOT(onLogLevelFilterChanged()));

    disconnect(d->SelectionModel.data(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
               this, SLOT(onSelectionChanged(QItemSelection,QItemSelection)));

    d->SelectionModel.clear();
    }

  d->ErrorLogTableView->setModel(newErrorLogModel);

  if (newErrorLogModel)
    {
    connect(this->errorLogModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
            this, SLOT(onRowsInserted(QModelIndex,int,int)));

    connect(this->errorLogModel(), SIGNAL(logLevelFilterChanged()),
            this, SLOT(onLogLevelFilterChanged()));

    ctkErrorLogLevel::LogLevels logLevelFilter = newErrorLogModel->logLevelFilter();
    this->setErrorEntriesVisible(logLevelFilter & d->ErrorButtonFilter);
    this->setWarningEntriesVisible(logLevelFilter & d->WarningButtonFilter);
    this->setInfoEntriesVisible(logLevelFilter & d->InfoButtonFilter);
    this->errorLogModel()->filterEntry(logLevelFilter & ctkErrorLogLevel::Unknown);

    // Setup selection model
    d->SelectionModel = QSharedPointer<QItemSelectionModel>(new QItemSelectionModel(this->errorLogModel()));
    d->SelectionModel->reset();

    connect(d->SelectionModel.data(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
            this, SLOT(onSelectionChanged(QItemSelection,QItemSelection)));

    d->ErrorLogTableView->setSelectionModel(d->SelectionModel.data());

    // Resize time column only if there are rows
    if (this->errorLogModel()->rowCount() > 0)
      {
      d->ErrorLogTableView->resizeColumnToContents(ctkErrorLogModel::TimeColumn);
      }
    }
  else
    {
    this->setAllEntriesVisible(0);
    }

  d->ErrorLogTableView->setColumnHidden(ctkErrorLogModel::ThreadIdColumn, true);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setColumnHidden(int columnId, bool hidden) const
{
  Q_D(const ctkErrorLogWidget);
  d->ErrorLogTableView->setColumnHidden(columnId, hidden);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setAllEntriesVisible(bool visibility)
{
  this->setErrorEntriesVisible(visibility);
  this->setWarningEntriesVisible(visibility);
  this->setInfoEntriesVisible(visibility);
  this->setUnknownEntriesVisible(visibility);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setErrorEntriesVisible(bool visibility)
{
  Q_D(ctkErrorLogWidget);
  if (!this->errorLogModel())
    {
    return;
    }
  this->errorLogModel()->filterEntry(d->ErrorButtonFilter, /* disableFilter= */ !visibility);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setWarningEntriesVisible(bool visibility)
{
  Q_D(ctkErrorLogWidget);
  if (!this->errorLogModel())
    {
    return;
    }
  this->errorLogModel()->filterEntry(d->WarningButtonFilter, /* disableFilter= */ !visibility);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setInfoEntriesVisible(bool visibility)
{
  Q_D(ctkErrorLogWidget);
  if (!this->errorLogModel())
    {
    return;
    }
  this->errorLogModel()->filterEntry(d->InfoButtonFilter, /* disableFilter= */ !visibility);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::setUnknownEntriesVisible(bool visibility)
{
  if (!this->errorLogModel())
    {
    return;
    }
  this->errorLogModel()->filterEntry(ctkErrorLogLevel::Unknown,
      /* disableFilter= */ !visibility);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::onRowsInserted(const QModelIndex &/*parent*/, int /*first*/, int /*last*/)
{
  Q_D(ctkErrorLogWidget);
  if (d->ErrorLogTableView->model()->rowCount() == 1)
    {
    // For performance reason, resize first column only when first entry is added
    d->ErrorLogTableView->resizeColumnToContents(ctkErrorLogModel::TimeColumn);
    }
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::onLogLevelFilterChanged()
{
  Q_D(ctkErrorLogWidget);
  Q_ASSERT(this->errorLogModel());
  ctkErrorLogLevel::LogLevels logLevelFilter = this->errorLogModel()->logLevelFilter();
  d->ShowErrorEntryButton->setChecked(logLevelFilter & d->ErrorButtonFilter);
  d->ShowWarningEntryButton->setChecked(logLevelFilter & d->WarningButtonFilter);
  d->ShowInfoEntryButton->setChecked(logLevelFilter & d->InfoButtonFilter);
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::removeEntries()
{
  Q_ASSERT(this->errorLogModel());
  this->errorLogModel()->clear();
}

// --------------------------------------------------------------------------
void ctkErrorLogWidget::onSelectionChanged(const QItemSelection & selected,
                                           const QItemSelection & deselected)
{
  // QTime start = QTime::currentTime();

  Q_D(ctkErrorLogWidget);
  Q_UNUSED(selected);
  Q_UNUSED(deselected);

  QModelIndexList selectedRows =
      d->SelectionModel->selectedRows(ctkErrorLogModel::DescriptionColumn);

  std::sort(selectedRows.begin(), selectedRows.end());

  QStringList descriptions;

  foreach(const QModelIndex& index, selectedRows)
    {
    descriptions << index.data(ctkErrorLogModel::DescriptionTextRole).toString();
    }

  d->ErrorLogDescription->setText(descriptions.join("\n"));

  // fprintf(stdout, "onSelectionChanged: %d\n", start.msecsTo(QTime::currentTime()));
}
<|endoftext|>


<commit_before>/* Copyright 2017 The Apollo Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=========================================================================*/
#include "modules/map/hdmap/hdmap_util.h"

#include "modules/common/util/file.h"
#include "modules/common/util/string_tokenizer.h"

namespace apollo {
namespace hdmap {
namespace {

// Find the first existing file from a list of candidates: "file_a|file_b|...".
std::string FindFirstExist(const std::string& dir, const std::string& files) {
  const auto candidates =
      apollo::common::util::StringTokenizer::Split(files, "|");
  for (const auto& filename : candidates) {
    const std::string file_path =
        apollo::common::util::StrCat(FLAGS_map_dir, "/", filename);
    if (apollo::common::util::PathExists(file_path)) {
      return file_path;
    }
  }
  AERROR << "No existing file found in " << dir << "/" << files;
  return "";
}

}  // namespace

std::string BaseMapFile() {
  return FindFirstExist(FLAGS_map_dir, FLAGS_base_map_filename);
}

std::string SimMapFile() {
  return FindFirstExist(FLAGS_map_dir, FLAGS_sim_map_filename);
}

std::string RoutingMapFile() {
  return FindFirstExist(FLAGS_map_dir, FLAGS_sim_map_filename);
}

std::unique_ptr<HDMap> CreateMap(const std::string& map_file_path) {
  std::unique_ptr<HDMap> hdmap(new HDMap());
  if (hdmap->load_map_from_file(map_file_path) != 0) {
    AERROR << "Failed to load HDMap " << map_file_path;
    hdmap.reset(nullptr);
  } else {
    AINFO << "Load HDMap succ.: " << map_file_path;
  }
  return hdmap;
}

HDMapUtil::HDMapUtil() {}

const HDMap* HDMapUtil::BaseMap() {
  if (base_map_ == nullptr) {
    std::lock_guard<std::mutex> lock(base_map_mutex_);
    if (base_map_ == nullptr) {  // Double check.
      base_map_ = CreateMap(BaseMapFile());
    }
  }
  return base_map_.get();
}

const HDMap& HDMapUtil::BaseMapRef() {
  return *CHECK_NOTNULL(BaseMap());
}

bool HDMapUtil::ReloadBaseMap() {
  std::lock_guard<std::mutex> lock(base_map_mutex_);
  base_map_ = CreateMap(BaseMapFile());
  return base_map_ != nullptr;
}

}  // namespace hdmap
}  // namespace apollo
<commit_msg>map: fix routing map file name.<commit_after>/* Copyright 2017 The Apollo Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=========================================================================*/
#include "modules/map/hdmap/hdmap_util.h"

#include "modules/common/util/file.h"
#include "modules/common/util/string_tokenizer.h"

namespace apollo {
namespace hdmap {
namespace {

// Find the first existing file from a list of candidates: "file_a|file_b|...".
std::string FindFirstExist(const std::string& dir, const std::string& files) {
  const auto candidates =
      apollo::common::util::StringTokenizer::Split(files, "|");
  for (const auto& filename : candidates) {
    const std::string file_path =
        apollo::common::util::StrCat(FLAGS_map_dir, "/", filename);
    if (apollo::common::util::PathExists(file_path)) {
      return file_path;
    }
  }
  AERROR << "No existing file found in " << dir << "/" << files;
  return "";
}

}  // namespace

std::string BaseMapFile() {
  return FindFirstExist(FLAGS_map_dir, FLAGS_base_map_filename);
}

std::string SimMapFile() {
  return FindFirstExist(FLAGS_map_dir, FLAGS_sim_map_filename);
}

std::string RoutingMapFile() {
  return FindFirstExist(FLAGS_map_dir, FLAGS_routing_map_filename);
}

std::unique_ptr<HDMap> CreateMap(const std::string& map_file_path) {
  std::unique_ptr<HDMap> hdmap(new HDMap());
  if (hdmap->load_map_from_file(map_file_path) != 0) {
    AERROR << "Failed to load HDMap " << map_file_path;
    hdmap.reset(nullptr);
  } else {
    AINFO << "Load HDMap succ.: " << map_file_path;
  }
  return hdmap;
}

HDMapUtil::HDMapUtil() {}

const HDMap* HDMapUtil::BaseMap() {
  if (base_map_ == nullptr) {
    std::lock_guard<std::mutex> lock(base_map_mutex_);
    if (base_map_ == nullptr) {  // Double check.
      base_map_ = CreateMap(BaseMapFile());
    }
  }
  return base_map_.get();
}

const HDMap& HDMapUtil::BaseMapRef() {
  return *CHECK_NOTNULL(BaseMap());
}

bool HDMapUtil::ReloadBaseMap() {
  std::lock_guard<std::mutex> lock(base_map_mutex_);
  base_map_ = CreateMap(BaseMapFile());
  return base_map_ != nullptr;
}

}  // namespace hdmap
}  // namespace apollo
<|endoftext|>