/**************************************************************************** * MeshLab o o * * A versatile mesh processing toolbox o o * * _ O _ * * Copyright(C) 2005 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * 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 (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #include "qualitymapperdialog.h" #include #include #include #include #include #include using namespace vcg; //returns true if relative x of h1 is < then x of h2. //if x values of h1 and h2 are the same, true is returned if relative y of h1 is < then relative y of h2 bool TfHandleCompare(TFHandle*h1, TFHandle*h2) { return (h1->getRelativeX() <= h2->getRelativeX()); } //callback to manage the double-click of TFDoubleClickCatcher object void TFDoubleClickCatcher::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { emit TFdoubleClicked(event->scenePos()); } //class constructor QualityMapperDialog::QualityMapperDialog(QWidget *parent, MeshModel& m, GLArea *gla,MLSceneGLSharedDataContext* cont) : QDockWidget(parent), _cont(cont), mesh(m) { ui.setupUi(this); this->setWidget(ui.frame); this->setFloating(true); // Setting dialog position in top right corner QPoint p=parent->mapToGlobal(QPoint(0,0)); this->setGeometry(p.x()+(parent->width()-width()),p.y()+40,width(),height() ); this->gla = gla; _histogram_info = 0; _equalizer_histogram = 0; for (int i=0; iinitTF(); //building a catcher for double click in empty areas of transfer function view //and adding it to the scene _tfCatcher = new TFDoubleClickCatcher(_transferFunction_info); _tfCatcher->setZValue(0); _transferFunctionScene.addItem(_tfCatcher); connect(_tfCatcher, SIGNAL(TFdoubleClicked(QPointF)), this, SLOT(on_TF_view_doubleClicked(QPointF))); // toggling Trackball button connect(this, SIGNAL(suspendEditToggle()),gla,SLOT(suspendEditToggle()) ); suspendEditToggle(); } //class destructor QualityMapperDialog::~QualityMapperDialog() { //removing from scene and deleting each graphical object this->clearItems(REMOVE_ALL_ITEMS | DELETE_REMOVED_ITEMS); //destroying info about histogram chart if ( _histogram_info ) { delete _histogram_info; _histogram_info = 0; } //destroying transfer function object if ( _transferFunction ) { delete _transferFunction; _transferFunction = 0; } //destroying info about histogram chart if ( _transferFunction_info ) { delete _transferFunction_info; _transferFunction_info = 0; } //destroying transfer function double-click catcher if ( _tfCatcher ) { delete _tfCatcher; _tfCatcher = 0; } // this->disconnect(); //sending closing dialog, to call EndEdit of plugin emit closingDialog(); } /* Resets histogram and builds a new one */ void QualityMapperDialog::ComputePerVertexQualityHistogram( CMeshO &m, Frange range, Histogramf *h, int bins ) // V1.0 { h->Clear(); h->SetRange( range.minV, range.maxV, bins); CMeshO::VertexIterator vi; for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD()) h->Add((*vi).Q()); } //clears the whole scene by removing and\or deleting all its items //returns a pointer to the list of removed items GRAPHICS_ITEMS_LIST* QualityMapperDialog::clearScene(QGraphicsScene *scene, int cleanFlag) { //deleting scene items GRAPHICS_ITEMS_LIST allItems = _transferFunctionScene.items(); QGraphicsItem *item = 0; foreach (item, allItems) { scene->removeItem( item ); _removed_items << item; } //deleting if necessary if ((cleanFlag & DELETE_REMOVED_ITEMS) == DELETE_REMOVED_ITEMS) { foreach (item, _removed_items) { delete item; item = 0; } } return &_removed_items; } /* Clears some particular types of graphical items from dialog. toClear value represents the logical OR of some macros that define what to clear returns a list of pointers to Items removed (NOT DELETED!!) If DELETE_REMOVED_ITEMS is passed in toClear flag, the items are destroyed too and the list returned is empty */ GRAPHICS_ITEMS_LIST* QualityMapperDialog::clearItems(int toClear) { _removed_items.clear(); QGraphicsItem *item = 0; if ((toClear & REMOVE_TF_HANDLE) == REMOVE_TF_HANDLE) { //removing TF Handles for (int i=0; idisconnect(); _transferFunctionScene.removeItem( item ); _removed_items << item; } _transferFunctionHandles[i].clear(); } } if ((toClear & REMOVE_EQ_HANDLE) == REMOVE_EQ_HANDLE) { //removing EQ Handles for (int i=0; idisconnect(); _equalizerHistogramScene.removeItem( (QGraphicsItem*)_equalizerHandles[i] ); _removed_items << _equalizerHandles[i]; } } if ((toClear & DELETE_REMOVED_ITEMS) == DELETE_REMOVED_ITEMS) { for (int i=0; iclearScene( &_equalizerHistogramScene, toClear & DO_NOT_DELETE_REMOVED_ITEMS )); } if ((toClear & DELETE_REMOVED_ITEMS) == DELETE_REMOVED_ITEMS) { //deleting removed items foreach(item, _removed_items) { if (item != 0) { delete item; item = 0; } } _removed_items.clear(); } return &_removed_items; } //draws the charts basics (axis) void QualityMapperDialog::drawChartBasics(QGraphicsScene& scene, CHART_INFO *chart_info) { //a valid chart_info must be passed assert( chart_info != 0 ); QPen p( Qt::black, 2 ); QGraphicsItem *current_item = 0; //drawing axis //x axis current_item = scene.addLine( chart_info->leftBorder(), chart_info->lowerBorder(), chart_info->rightBorder(), chart_info->lowerBorder(), p ); current_item->setZValue( 0 ); if (chart_info == _transferFunction_info) _transferFunctionLines << current_item; else _equalizerHistogramBars << current_item; //y axis current_item = scene.addLine( chart_info->leftBorder(), chart_info->upperBorder(), chart_info->leftBorder(), chart_info->lowerBorder(), p ); current_item->setZValue( 0 ); if (chart_info == _transferFunction_info) _transferFunctionLines << current_item; else _equalizerHistogramBars << current_item; } // Initializes equalizerHistogramView // (This method is called only once) bool QualityMapperDialog::initEqualizerHistogram() { if (_equalizer_histogram) //added by MAL 17\02\08 { // _equalizer_histogram->disconnect(); //added by MAL 17\02\08 delete _equalizer_histogram; //added by MAL 17\02\08 _equalizer_histogram = 0; } _leftHandleWasInsideHistogram = true; _rightHandleWasInsideHistogram = true; if ( !drawEqualizerHistogram(true, true) ) return false; //DRAWING HANDLES QDoubleSpinBox* spinboxes[] = { ui.minSpinBox, ui.midSpinBox, ui.maxSpinBox }; qreal xStart = _histogram_info->leftBorder(); qreal xPos = 0.0f; qreal yPos = _histogram_info->lowerBorder(); _equalizerMidHandlePercentilePosition = 0.5f; for (int i=0; ichartWidth()/2.0f*i; _equalizerHandles[i] = new EqHandle(_histogram_info, Qt::black, QPointF(xPos, yPos), (EQUALIZER_HANDLE_TYPE)i, _equalizerHandles, &_equalizerMidHandlePercentilePosition, spinboxes[i], 1, 5); _equalizerHistogramScene.addItem((QGraphicsItem*)_equalizerHandles[i]); } // SETTING SPNIBOX VALUES // (Se venissero inizializzati prima di impostare setHistogramInfo sulle handles darebbero errore nello SLOT setX delle handles.) initEqualizerSpinboxes(); //SETTING UP CONNECTIONS // Connecting spinboxes to handles connect(ui.minSpinBox, SIGNAL(valueChanged(double)), _equalizerHandles[LEFT_HANDLE], SLOT(setXBySpinBoxValueChanged(double))); connect(ui.midSpinBox, SIGNAL(valueChanged(double)), _equalizerHandles[MID_HANDLE], SLOT(setXBySpinBoxValueChanged(double))); connect(ui.maxSpinBox, SIGNAL(valueChanged(double)), _equalizerHandles[RIGHT_HANDLE], SLOT(setXBySpinBoxValueChanged(double))); // Connecting handles to spinboxes connect(_equalizerHandles[LEFT_HANDLE], SIGNAL(positionChangedToSpinBox(double)), ui.minSpinBox, SLOT(setValue(double))); connect(_equalizerHandles[MID_HANDLE], SIGNAL(positionChangedToSpinBox(double)), ui.midSpinBox, SLOT(setValue(double))); connect(_equalizerHandles[MID_HANDLE], SIGNAL(positionChangedToSpinBox(double)), this, SLOT(on_midSpinBox_valueChanged(double))); connect(_equalizerHandles[RIGHT_HANDLE], SIGNAL(positionChangedToSpinBox(double)), ui.maxSpinBox, SLOT(setValue(double))); // Connecting left and right handles to mid handle connect(_equalizerHandles[LEFT_HANDLE], SIGNAL(positionChanged()), _equalizerHandles[MID_HANDLE], SLOT(moveMidHandle())); connect(_equalizerHandles[RIGHT_HANDLE], SIGNAL(positionChanged()), _equalizerHandles[MID_HANDLE], SLOT(moveMidHandle())); // Making spinboxes and handles changes redrawing transferFunctionScene // NotE: it is not necessary to connect spinboxes too connect(_equalizerHandles[LEFT_HANDLE], SIGNAL(positionChanged()), this, SLOT(on_EQHandle_moved())); connect(_equalizerHandles[MID_HANDLE], SIGNAL(positionChanged()), this, SLOT(on_EQHandle_moved())); connect(_equalizerHandles[RIGHT_HANDLE], SIGNAL(positionChanged()), this, SLOT(on_EQHandle_moved())); // Connecting mid equalizerHistogram handle to gammaCorrectionLabel connect(_equalizerHandles[MID_HANDLE], SIGNAL(positionChanged()), this, SLOT(drawGammaCorrection()) ); connect(ui.midSpinBox, SIGNAL(valueChanged(double)), this, SLOT(drawGammaCorrection()) ); // Connecting eqHandles to histogram drawing connect(_equalizerHandles[LEFT_HANDLE], SIGNAL(insideHistogram(EqHandle*,bool)), this, SLOT(on_EqHandle_crossing_histogram(EqHandle*,bool)) ); connect(_equalizerHandles[RIGHT_HANDLE], SIGNAL(insideHistogram(EqHandle*,bool)), this, SLOT(on_EqHandle_crossing_histogram(EqHandle*,bool)) ); // Connecting handles to preview method connect(_equalizerHandles[LEFT_HANDLE], SIGNAL(handleReleased()), this, SLOT(meshColorPreview())); connect(_equalizerHandles[MID_HANDLE], SIGNAL(handleReleased()), this, SLOT(meshColorPreview())); connect(_equalizerHandles[RIGHT_HANDLE], SIGNAL(handleReleased()), this, SLOT(meshColorPreview())); connect(ui.brightnessSlider, SIGNAL(sliderReleased()), this, SLOT(meshColorPreview())); connect(ui.brightessSpinBox, SIGNAL(valueChanged(double)), this, SLOT(meshColorPreview())); connect(ui.minSpinBox, SIGNAL(editingFinished()), this, SLOT(on_previewButton_clicked())); connect(ui.midSpinBox, SIGNAL(editingFinished()), this, SLOT(on_previewButton_clicked())); connect(ui.maxSpinBox, SIGNAL(editingFinished()), this, SLOT(on_previewButton_clicked())); // Connecting brightessSpinBox to brightnessSlider connect(ui.brightessSpinBox, SIGNAL(valueChanged(double)), this, SLOT(on_brightessSpinBox_valueChanged(double))); ui.equalizerGraphicsView->setScene(&_equalizerHistogramScene); drawGammaCorrection(); drawTransferFunctionBG(); return true; } void QualityMapperDialog::initEqualizerSpinboxes() { double singleStep = (_histogram_info->maxX - _histogram_info->minX) / _histogram_info->chartWidth(); int decimals = 0; if (singleStep > std::numeric_limits::epsilon()) { double temp = singleStep; while (temp < 0.1) { decimals++; temp *= 10; } } decimals+=2; ui.minSpinBox->setDecimals(decimals); ui.minSpinBox->setValue(_histogram_info->minX); ui.minSpinBox->setRange(numeric_limits::min(), _histogram_info->maxX); //ui.minSpinBox->setRange(2*_histogram_info->minX - _histogram_info->maxX, 2*_histogram_info->maxX - _histogram_info->minX); ui.minSpinBox->setSingleStep(singleStep); ui.maxSpinBox->setDecimals(decimals); ui.maxSpinBox->setValue(_histogram_info->maxX); ui.maxSpinBox->setRange(_histogram_info->minX, numeric_limits::max()); //ui.maxSpinBox->setRange(2*_histogram_info->minX - _histogram_info->maxX, 2*_histogram_info->maxX - _histogram_info->minX); ui.maxSpinBox->setSingleStep(singleStep); ui.midSpinBox->setDecimals(decimals); ui.midSpinBox->setValue((_histogram_info->maxX + _histogram_info->minX) / 2.0f); ui.midSpinBox->setRange(_histogram_info->minX, _histogram_info->maxX); ui.midSpinBox->setSingleStep(singleStep); } // Add histogram bars to equalizerHistogram Scene // Return false if mesh has no quality => dialog will not be built bool QualityMapperDialog::drawEqualizerHistogram(bool leftHandleIsInsideHistogram, bool rightHandleIsInsideHistogram) { if (_equalizer_histogram == 0) { // This block is called only the first time _equalizer_histogram = new Histogramf(); Frange histogramRange(tri::Stat::ComputePerVertexQualityMinMax(mesh.cm)); this->ComputePerVertexQualityHistogram(mesh.cm, histogramRange, _equalizer_histogram, NUMBER_OF_HISTOGRAM_BINS); if (histogramRange.minV == histogramRange.maxV) { QMessageBox::warning(this, tr("Quality Mapper"), tr("The model vertex quality is a constant"), QMessageBox::Ok); return false; } //building histogram chart information _histogram_info = new CHART_INFO( ui.equalizerGraphicsView, _equalizer_histogram->MinV(), _equalizer_histogram->MaxV(), 0, computeEqualizerMaxY(_equalizer_histogram, _equalizer_histogram->MinV(), _equalizer_histogram->MaxV()) ); } else { //added by MAL 23/04/08 _transferFunction_info->updateChartInfo(_equalizer_histogram->MinV(), _equalizer_histogram->MaxV(), 0, computeEqualizerMaxY(_equalizer_histogram, _equalizer_histogram->MinV(), _equalizer_histogram->MaxV()) ); // if histogram doesn't need to be redrawn, return if ( (leftHandleIsInsideHistogram && _leftHandleWasInsideHistogram) && (rightHandleIsInsideHistogram && _rightHandleWasInsideHistogram) ) return true; // Updating flags _leftHandleWasInsideHistogram [right] with new values _leftHandleWasInsideHistogram = leftHandleIsInsideHistogram; _rightHandleWasInsideHistogram = rightHandleIsInsideHistogram; this->clearItems( REMOVE_EQ_HISTOGRAM | DELETE_REMOVED_ITEMS ); // removing and deleting histogram bars // Calculating new Histogram //float minX = (_histogram_info->minXvalue())?_histogram_info->minX:ui.minSpinBox->value(); //float maxX = (_histogram_info->maxX>ui.maxSpinBox->value())?_histogram_info->maxX:ui.maxSpinBox->value(); //Frange histogramRange(minX, maxX); //this->ComputePerVertexQualityHistogram(mesh.cm, histogramRange, _equalizer_histogram, NUMBER_OF_HISTOGRAM_BINS); // Only minY and maxY are modified in _histogram_info //_histogram_info->maxY = computeEqualizerMaxY(_equalizer_histogram, minX, maxX); } float minX = (_histogram_info->minXvalue())?_histogram_info->minX:ui.minSpinBox->value(); float maxX = (_histogram_info->maxX>ui.maxSpinBox->value())?_histogram_info->maxX:ui.maxSpinBox->value(); _histogram_info->maxY = computeEqualizerMaxY(_equalizer_histogram, minX, maxX); //drawing axis and other basic items this->drawChartBasics( _equalizerHistogramScene, _histogram_info ); //drawing histogram bars this->drawHistogramBars (_equalizerHistogramScene, _histogram_info, minX, maxX, QColor(192,192,192)); // this->drawGammaCorrection(); this->drawTransferFunctionBG(); return true; } int QualityMapperDialog::computeEqualizerMaxY (Histogramf *h, float minX, float maxX) { float barRange = (maxX-minX)/(float)NUMBER_OF_HISTOGRAM_BARS; int maxY = 0; int currentY; for (int i=0; iBinCount( minX+i*barRange, barRange ); if (currentY>maxY) maxY = currentY; } return maxY; } // Add histogramBars to destinationScene with GAMMA-STRETCHING void QualityMapperDialog::drawHistogramBars (QGraphicsScene& destinationScene, CHART_INFO *chartInfo, float minVal, float maxVal, QColor color) { float barHeight = 0.0f; //initializing height of the histogram bars float barWidth = chartInfo->chartWidth() / (float)(NUMBER_OF_HISTOGRAM_BARS); //processing width of the histogram bars // exp is such that: _equalizerMidHandlePercentilePosition^exp = 0.5 float exp = log10(0.5f) / log10((float)_equalizerMidHandlePercentilePosition); QPen drawingPen(color); QBrush drawingBrush (color); QPointF startBarPt; //drawing histogram bars QGraphicsItem *current_item = 0; float barRange = (maxVal-minVal)/(float)NUMBER_OF_HISTOGRAM_BARS; for (int i = 0; i < NUMBER_OF_HISTOGRAM_BARS; i++) { // Setting barHeight proportional to max height barHeight = (float)(chartInfo->chartHeight() * _equalizer_histogram->BinCount( minVal+i*barRange, barRange) ) / (float)chartInfo->maxY; startBarPt.setY( (float)chartInfo->lowerBorder() - barHeight ); //drawing histogram bar if ( &destinationScene == &_transferFunctionScene ) { // Histogram bars positions are calculted applying an exponential function: relIndex^exp startBarPt.setX( chartInfo->leftBorder() + relative2AbsoluteValf(pow( absolute2RelativeValf(i,NUMBER_OF_HISTOGRAM_BARS ), exp ), chartInfo->chartWidth()) ); current_item = destinationScene.addLine(startBarPt.x(), startBarPt.y(), startBarPt.x(), (float)chartInfo->lowerBorder(), drawingPen); _transferFunctionBg << current_item; } else // ( &destinationScene == &_equalizerHistogramScene ) { // histogram bars are added all at same distance startBarPt.setX( chartInfo->leftBorder() + ( barWidth * (i) ) ); current_item = destinationScene.addRect(startBarPt.x(), startBarPt.y(), barWidth, barHeight, drawingPen, drawingBrush); _equalizerHistogramBars << current_item; } current_item->setZValue(-1); } } //Prepares the Transfer Function unit to work void QualityMapperDialog::initTF() { assert(_transferFunction != 0); //UPDATE OF PRESET COMBO BOX STATE //blocking comboBox signals ui.presetComboBox->blockSignals( true ); //adding default TFs text to comboBox QString itemText; for (int i=0; ifindText( itemText ) ) ui.presetComboBox->addItem( itemText ); } //adding to comboBox "Known External TFs", i.e. TFs previously loaded from external TF Files //for each external file TF the system stores the path of the file and its name (this one is added to comboBox) for (int i=0; i<_knownExternalTFs.size(); i++) { //fetching KETF from KETFs list itemText = _knownExternalTFs.at(i).name; //items are added to the list only if they're not present yet if ( -1 == ui.presetComboBox->findText( itemText ) ) ui.presetComboBox->insertItem( 0, itemText ); } //comboBox operations complete. Re-enabling signals for comboBox ui.presetComboBox->blockSignals( false ); //building transfer function chart information (if necessary) if ( _transferFunction_info == 0 ) _transferFunction_info = new CHART_INFO( ui.transferFunctionView, /*_transferFunction->size(),*/ 0.0f, 1.0f, 0.0f, 1.0f ); //removing and deleting any old TF graphics item (TF lines and handles) this->clearItems( REMOVE_TF_ALL | DELETE_REMOVED_ITEMS ); //passing to TFHandles the pointer to the current Transfer Function assert(_transferFunction != 0); TFHandle::setTransferFunction(_transferFunction); //setting up handles TF_KEY *val = 0; QColor channelColor; qreal zValue = 0; for (int c=0; cgetChannel(c).size(); i++) { val = _transferFunction->getChannel(c)[i]; this->addTfHandle( c, QPointF(_transferFunction_info->leftBorder() + relative2AbsoluteValf( val->x, (float)_transferFunction_info->chartWidth() ), _transferFunction_info->lowerBorder() - relative2AbsoluteValf( val->y, (float)_transferFunction_info->chartHeight() )), val, zValue ); } } //adding to TF Scene TFHandles //if handles are not already in the transferFunctionScene, they're added to it if ( ! _transferFunctionScene.items().contains(_transferFunctionHandles[0][0]) ) for (int i=0; i 0 ) for (int h=0; h<_transferFunctionHandles[i].size(); h++) _transferFunctionScene.addItem( _transferFunctionHandles[i][h] ); //no TfHande selected yet _currentTfHandle = 0; //all done _isTransferFunctionInitialized = true; //refreshing TF BG this->drawTransferFunctionBG(); //initializing forward channel to BLUE ui.blueButton->setChecked( true ); } /* Draws gammaCorrection only depending on equalizerMidHandlePercentilePosition and using a quadric spline */ void QualityMapperDialog::drawGammaCorrection() { int width = ui.gammaCorrectionLabel->width(); int height = ui.gammaCorrectionLabel->height(); QPixmap pixmap(width, height); pixmap.fill(); QPainter painter(&pixmap); painter.setOpacity(1.0); painter.setPen(Qt::black); painter.drawRect(0,0,width-1,height-1); painter.setPen(QColor(128,128,128)); painter.drawLine(0,height-1,width-1,0); //// Painting the spline representing the exponential function: x^exp, 0<=x<=1 int c = _equalizerMidHandlePercentilePosition*width; QPainterPath path; path.moveTo(0, height); path.quadTo(c, c, width, 0); painter.setPen(QColor(255,0,0)); painter.drawPath(path); ////painter.end(); ui.gammaCorrectionLabel->setPixmap(pixmap); /*delete pixmap;*/ } //draws the Transfer Function in the transfer function view void QualityMapperDialog::drawTransferFunction() { //before drawing, old TF lines are removed and destroyed this->clearItems( REMOVE_TF_LINES | DELETE_REMOVED_ITEMS ); //TF must exist! assert(_transferFunction != 0); //building transfer function chart information if ( _transferFunction_info == 0 ) _transferFunction_info = new CHART_INFO( ui.transferFunctionView, /* _transferFunction->size(),*/ 0.0f, 1.0f, 0.0f, 1.0f ); else // added by MAL 23/04/08 _transferFunction_info->updateChartInfo( /* _transferFunction->size(),*/ 0.0f, 1.0f, 0.0f, 1.0f ); //is necessary, initialize TF if ( !_isTransferFunctionInitialized ) this->initTF(); //drawing axis and other basic items this->drawChartBasics( _transferFunctionScene, _transferFunction_info ); QColor channelColor; QPen drawingPen(Qt::black, 3); QGraphicsItem *item = 0; QGraphicsItem *handle1 = 0; QGraphicsItem *handle2 = 0; qreal zValue = 0; int channelType = 0; //for each channel for(int c=0; csetZValue( zValue); //if there's another handle after this in the sequence if ( (i+1)<_transferFunctionHandles[channelType].size() ) { //fetching 2nd handle handle2 = _transferFunctionHandles[channelType][i+1]; //setting z-order handle1->setZValue( zValue+1 ); //modified by MAL 15/02/08 //fetching positions of handles pos1 = handle1->scenePos(); pos2 = handle2->scenePos(); //highlighting line if it touches the currently selected handle if (( handle1 == _currentTfHandle ) || (handle2 == _currentTfHandle) ) drawingPen.setColor( channelColor.lighter() ); else drawingPen.setColor( channelColor ); //tracing line between the 2 handles item = _transferFunctionScene.addLine( handle1->scenePos().x(), handle1->scenePos().y(), handle2->scenePos().x(), handle2->scenePos().y(), drawingPen ); //setting z-order of line item->setZValue( zValue ); //adding line to lines list _transferFunctionLines << item; } } } // updating Color Band this->updateColorBand(); //_transferFunctionScene.addRect(0.5,0.5,0.2,0.2); ui.transferFunctionView->setScene( &_transferFunctionScene ); ui.transferFunctionView->update(); } /* Updates color band label */ void QualityMapperDialog::updateColorBand() { // Calculating the vector of colors representing the transfer function QColor* colors = _transferFunction->buildColorBand(); // Building the image that will represent to be inserted in the label QImage image(ui.colorbandLabel->width(), 1, QImage::Format_RGB32); float step = ((float)COLOR_BAND_SIZE) / ((float)ui.colorbandLabel->width()); // Setting each pixel of the image for (int i=0; isetPixmap(QPixmap::fromImage(image)); } /* Draws the partial histogram as background of the transfer function scene */ void QualityMapperDialog::drawTransferFunctionBG () { this->clearItems( REMOVE_TF_BG | DELETE_REMOVED_ITEMS ); if (_histogram_info !=0) { // int minIndex = _equalizer_histogram->Interize((float)ui.minSpinBox->value()); // int maxIndex = _equalizer_histogram->Interize((float)ui.maxSpinBox->value()); // drawHistogramBars (_transferFunctionScene, _transferFunction_info, minIndex, maxIndex, QColor(192,192,192)); _transferFunction_info->maxY = this->computeEqualizerMaxY(_equalizer_histogram, (float)ui.minSpinBox->value(), (float)ui.maxSpinBox->value()); drawHistogramBars (_transferFunctionScene, _transferFunction_info, (float)ui.minSpinBox->value(), (float)ui.maxSpinBox->value(), QColor(192,192,192)); } } //callback for flip button click void QualityMapperDialog::on_flipButton_clicked() { // flip values in ramp _transferFunction->flipRamp(); //preparing TF to work this->initTF(); //drawing new TF this->drawTransferFunction(); //applying preview if necessary if (ui.previewButton->isChecked()) on_applyButton_clicked(); } //callback for save button click void QualityMapperDialog::on_savePresetButton_clicked() { //setting default save name QString tfName = ui.presetComboBox->currentText(); //user chooses the file to save and saves it onto disk EQUALIZER_INFO eqInfo; eqInfo.minQualityVal = ui.minSpinBox->value(); eqInfo.midQualityPercentage = _equalizerMidHandlePercentilePosition; eqInfo.maxQualityVal = ui.maxSpinBox->value(); eqInfo.brightness = (1.0f - (float)(ui.brightnessSlider->value())/(float)(ui.brightnessSlider->maximum()) )*2.0; QString tfPath = _transferFunction->saveColorBand( tfName, eqInfo ); //user didn't select anything. Nothing to do. if (tfPath.isNull()) return ; //building file info QFileInfo fi(tfPath); tfName = fi.fileName(); QString ext = CSV_FILE_EXSTENSION; if ( tfName.endsWith( ext ) ) tfName.remove( tfName.size() - ext.size(), ext.size() ); //adding external file to the list of known ones KNOWN_EXTERNAL_TFS newTF( tfPath, tfName ); _knownExternalTFs << newTF; this->clearItems( REMOVE_TF_ALL | DELETE_REMOVED_ITEMS ); //preparing TF to work _isTransferFunctionInitialized = false; this->initTF(); ui.presetComboBox->setCurrentIndex( 0 ); } //callback for loading a new TF from an external CSV file void QualityMapperDialog::on_loadPresetButton_clicked() { //user chooses the file to load QString csvFileName = QFileDialog::getOpenFileName(0, "Open Transfer Function File", QDir::currentPath(), QString("Quality Mapper File (*") + QString(CSV_FILE_EXSTENSION) + QString(")") ); //user didn't select anything. Nothing to do. if (csvFileName.isNull()) return ; //deleting any previous TF object if ( _transferFunction ) delete _transferFunction; //building new TF object from external file _transferFunction = new TransferFunction( csvFileName ); //building file info QFileInfo fi(csvFileName); QString tfName = fi.fileName(); QString ext = CSV_FILE_EXSTENSION; if ( tfName.endsWith( ext ) ) tfName.remove( tfName.size() - ext.size(), ext.size() ); //adding external file to the list of known ones KNOWN_EXTERNAL_TFS newTF( csvFileName, tfName ); _knownExternalTFs << newTF; //preparing TF to work _isTransferFunctionInitialized = false; this->initTF(); //setting combo box to TF just built ui.presetComboBox->setCurrentIndex( 0 ); //setting equalizer values EQUALIZER_INFO eqData; loadEqualizerInfo(csvFileName, &eqData); eqData.brightness = ui.brightnessSlider->maximum() * (1.0f - (eqData.brightness / 2.0f)); this->setEqualizerParameters(eqData); /* on_resetButton_clicked(csvFileName, &eqData);*/ //drawing new TF this->drawTransferFunction(); //applying preview if necessary if (ui.previewButton->isChecked()) //added by FB 07\02\08 on_applyButton_clicked(); } //callback to manage the user selection of a TF from combo box //builds a new TF joined to newValue. It searches first among the default ones, then among the external file ones void QualityMapperDialog::on_presetComboBox_currentTextChanged(const QString &newValue) { //searching newValue among default TFs for (int i=0; iinitTF(); //added by MAL 04\02\08 //drawing new TF this->drawTransferFunction(); //applying preview if necessary if (ui.previewButton->isChecked()) //added by FB 07\02\08 on_applyButton_clicked(); return ; } } //TF selected is not a default one. Maybe it's a loaded or saved one. Searching among known external TFs KNOWN_EXTERNAL_TFS external_tf; for (int i=0; i<_knownExternalTFs.size(); i++) { external_tf = _knownExternalTFs.at(i); //found it! if ( newValue == external_tf.name ) { //deleting any previous TF object if ( _transferFunction ) delete _transferFunction; //building new TF object from external file _transferFunction = new TransferFunction( external_tf.path ); //preparing TF to work this->initTF(); //added by MAL 04\02\08 //drawing new TF this->drawTransferFunction(); //applying preview if necessary if (ui.previewButton->isChecked()) //added by FB 07\02\08 on_applyButton_clicked(); return ; } } } //callback for RED radio button void QualityMapperDialog::on_redButton_toggled(bool checked) { //if checked moves ahead the RED CHANNEL TF items if (checked) this->moveAheadChannel( RED_CHANNEL ); } //callback for GREEN radio button void QualityMapperDialog::on_greenButton_toggled(bool checked) { //if checked moves ahead the GREEN CHANNEL TF items if (checked) this->moveAheadChannel( GREEN_CHANNEL ); } //callback for BLUE radio button void QualityMapperDialog::on_blueButton_toggled(bool checked) { //if checked moves ahead the BLUE CHANNEL TF items if (checked) this->moveAheadChannel( BLUE_CHANNEL ); } //bring in first plane the TfHandles and Tf lines of the channel passed to it void QualityMapperDialog::moveAheadChannel( TF_CHANNELS channelCode ) { //if Transfer Function object instanced if ( _transferFunction ) { //changing drawing order for channel lines _transferFunction->moveChannelAhead( channelCode ); //changing z order of TF handles QGraphicsItem *item = 0; for (int i=0; isetZValue(10.0); else item->setZValue(((i + 1)*2.0f) + 1); } //all done. Drawing updated TF this->drawTransferFunction(); } } /* Redraws histogram background of transfer function scene and updates x-quality Label */ void QualityMapperDialog::on_EQHandle_moved() { if ( _transferFunction ) // Redrawing histogram background of transfer function scene this->drawTransferFunctionBG(); if ( _currentTfHandle ) //updating x-quality Label this->updateXQualityLabel(_currentTfHandle->getRelativeX()); } //callback to manage move of a TfHandle object //updates the object position in the position spinboxes, restores the correct order among the TfHandle objects and refreshes the TF scene void QualityMapperDialog::on_TfHandle_moved(TFHandle *sender) { //suspending signals from sender sender->blockSignals( true ); //setting position spinboxes to Handle position ui.xSpinBox->blockSignals( true ); ui.xSpinBox->setValue(sender->getRelativeX()); ui.xSpinBox->blockSignals( false ); ui.ySpinBox->blockSignals( true ); ui.ySpinBox->setValue(sender->getRelativeY()); ui.ySpinBox->blockSignals( false ); this->manageBorderTfHandles(sender); //updating correct order among TF Handle objects this->updateTfHandlesOrder(sender->getChannel()); //refreshing TF scene this->drawTransferFunction(); //all done. Unlocking sender signals sender->blockSignals( false ); this->updateXQualityLabel(sender->getRelativeX()); } void QualityMapperDialog::manageBorderTfHandles(TFHandle *handle) { TF_KEY *firstKey = 0; TF_KEY *newKey = 0; if ( _transferFunction->getChannel(handle->getChannel()).size() > 0 ) { firstKey = _transferFunction->getChannel(handle->getChannel())[size_t(0)]; if ( handle->getMyKey() == firstKey ) { if ( ! _transferFunction->getChannel(handle->getChannel()).isHead(/*firstKey*/handle->getMyKey()) ) { newKey = new TF_KEY(0.0f, handle->getRelativeY()); _transferFunction->getChannel(handle->getChannel()).addKey(newKey); this->addTfHandle( handle->getChannel(), QPointF(_transferFunction_info->leftBorder() + relative2AbsoluteValf( 0.0f, (float)_transferFunction_info->chartWidth() ), _transferFunction_info->lowerBorder() - relative2AbsoluteValf( handle->getRelativeY(), (float)_transferFunction_info->chartHeight() )), newKey, ((handle->getChannel() + 1)*2.0f) + 1 ); } } } TF_KEY *lastKey = 0; if ( _transferFunction->getChannel(handle->getChannel()).size() > 0 ) { lastKey = _transferFunction->getChannel(handle->getChannel())[_transferFunction->getChannel(handle->getChannel()).size()-1]; if ( handle->getMyKey() == lastKey ) { if ( ! _transferFunction->getChannel(handle->getChannel()).isTail(/*lastKey*/handle->getMyKey()) ) { newKey = new TF_KEY(1.0f, handle->getRelativeY()); _transferFunction->getChannel(handle->getChannel()).addKey(newKey); this->addTfHandle( handle->getChannel(), QPointF(_transferFunction_info->leftBorder() + relative2AbsoluteValf( 1.0f, (float)_transferFunction_info->chartWidth() ), _transferFunction_info->lowerBorder() - relative2AbsoluteValf( handle->getRelativeY(), (float)_transferFunction_info->chartHeight() )), newKey, ((handle->getChannel() + 1)*2.0f) + 1); } } } } //callback to manage click on a TfHandle object //updates the currenttfHandle attribute and refresh the position spinboxes void QualityMapperDialog::on_TfHandle_clicked(TFHandle *sender) { if (_currentTfHandle) _currentTfHandle->setCurrentlSelected( false ); //updating currentTfHandle to sender _currentTfHandle = sender; _currentTfHandle->setCurrentlSelected( true ); //setting position spinboxes to Handle position ui.xSpinBox->setValue(_currentTfHandle->getRelativeX()); ui.ySpinBox->setValue(_currentTfHandle->getRelativeY()); //moving handle channel in foreground this->moveAheadChannel((TF_CHANNELS)sender->getChannel()); this->updateXQualityLabel(_currentTfHandle->getRelativeX()); //applying preview if necessary if (ui.previewButton->isChecked()) //added by FB 07\02\08 on_applyButton_clicked(); } //callback to manage double-click on a TfHandle object //updates the currenttfHandle attribute and refresh the position spinboxes void QualityMapperDialog::on_TfHandle_doubleClicked(TFHandle *sender) { //updating currentTfHandle to sender _currentTfHandle = sender; //removing sender _currentTfHandle = this->removeTfHandle(_currentTfHandle); //applying preview if necessary if (ui.previewButton->isChecked()) //added by FB 07\02\08 on_applyButton_clicked(); } void QualityMapperDialog::on_applyButton_clicked() { float minQuality = ui.minSpinBox->value(); float maxQuality = ui.maxSpinBox->value(); // brightness value between 0 and 2 float brightness = (1.0f - (float)(ui.brightnessSlider->value())/(float)(ui.brightnessSlider->maximum()) )*2.0; applyColorByVertexQuality(mesh, _transferFunction, minQuality, maxQuality, (float)_equalizerMidHandlePercentilePosition, brightness); MLRenderingData::RendAtts atts; atts[MLRenderingData::ATT_NAMES::ATT_VERTCOLOR] = true; //emit updateRequested(mesh.id(),MLRenderingData::ATT_NAMES::ATT_VERTCOLOR); _cont->meshAttributesUpdated(mesh.id(), false, atts); _cont->manageBuffers(mesh.id()); gla->updateAllSiblingsGLAreas(); } /* If preview button is toggled, apply color to mesh when a generic handle is released */ void QualityMapperDialog::meshColorPreview() { //applying preview if necessary if (ui.previewButton->isChecked()) on_applyButton_clicked(); } void QualityMapperDialog::on_previewButton_clicked() { if (ui.previewButton->isChecked()) on_applyButton_clicked(); } //callback that manages the value change of current Handle y position void QualityMapperDialog::on_xSpinBox_valueChanged(double newX) { // if any handle is selected if (_currentTfHandle) { //updating handle position in the scene _currentTfHandle->setPos(_transferFunction_info->leftBorder()+relative2AbsoluteValf(newX,_transferFunction_info->chartWidth()), _currentTfHandle->scenePos().y()); //updating the Tf Handle position at logical level (update of joined TF_KEY) _currentTfHandle->updateTfHandlesState(_currentTfHandle->scenePos()); this->manageBorderTfHandles(_currentTfHandle); //restoring the correct order for TfHandles (they're drawn in the same order as they're stored) this->updateTfHandlesOrder(_currentTfHandle->getChannel()); //updating x-quality Label this->updateXQualityLabel(_currentTfHandle->getRelativeX()); //refresh of TF this->drawTransferFunction(); //applying preview if necessary if (ui.previewButton->isChecked()) //added by FB 07\02\08 on_applyButton_clicked(); } } //callback that manages the value change of current Handle y position void QualityMapperDialog::on_ySpinBox_valueChanged(double newY) { // if any handle is selected if (_currentTfHandle) { //updating handle position in the scene _currentTfHandle->setPos(_currentTfHandle->scenePos().x(), _transferFunction_info->chartHeight()+_transferFunction_info->upperBorder()-relative2AbsoluteValf(newY,_transferFunction_info->chartHeight())); //updating the Tf Handle position at logical level (update of joined TF_KEY) _currentTfHandle->updateTfHandlesState(_currentTfHandle->scenePos()); //restoring the correct order for TfHandles (they're drawn in the same order as they're stored) this->manageBorderTfHandles(_currentTfHandle); this->updateTfHandlesOrder(_currentTfHandle->getChannel()); //refresh of TF this->drawTransferFunction(); //applying preview if necessary if (ui.previewButton->isChecked()) //added by FB 07\02\08 on_applyButton_clicked(); } } //orders the TfHandles by the x value of the joined TF_KEY void QualityMapperDialog::updateTfHandlesOrder(int channelCode) { //ordering TfHandles list (sort is used because of the tiny number of elements to manage) qSort(_transferFunctionHandles[channelCode].begin(), _transferFunctionHandles[channelCode].end(), TfHandleCompare); } //Resets original positions and values of equalizer handles, spinboxes and brightness slider void QualityMapperDialog::on_resetButton_clicked() { assert(_histogram_info != 0); EQUALIZER_INFO data; data.brightness = 50; data.minQualityVal = _histogram_info->minX; data.maxQualityVal = _histogram_info->maxX; data.midQualityPercentage = 0.5f; this->setEqualizerParameters(data); } // Method invoked when moving left/right EqHandles. It calls drawEqualizerHistogram with the correct parameters void QualityMapperDialog::on_EqHandle_crossing_histogram(EqHandle* sender, bool insideHistogram) { if (sender == _equalizerHandles[LEFT_HANDLE]) drawEqualizerHistogram(insideHistogram, _rightHandleWasInsideHistogram); else drawEqualizerHistogram(_leftHandleWasInsideHistogram, insideHistogram); } //adds a TFHandle to the TF scene with the properties defined by passed parameters //returns a pointer to the handle just added TFHandle* QualityMapperDialog::addTfHandle(int channelCode, QPointF handlePos, TF_KEY *key, int zOrder ) { QColor channelColor; //converting channel code into proper color TYPE_2_COLOR(channelCode, channelColor); return this->addTfHandle( new TFHandle( _transferFunction_info, channelColor, handlePos, key, zOrder ) ); } //adds a TFHandle to the TF scene with the properties defined by passed parameters //returns a pointer to the handle just added TFHandle* QualityMapperDialog::addTfHandle(TFHandle *handle) { //adding new handle to list of TF Handless of the belonging channel _transferFunctionHandles[handle->getChannel()] << handle; //preparing handle to manage events connect(handle, SIGNAL(positionChanged(TFHandle*)), this, SLOT(on_TfHandle_moved(TFHandle*))); connect(handle, SIGNAL(clicked(TFHandle*)), this, SLOT(on_TfHandle_clicked(TFHandle*))); connect(handle, SIGNAL(doubleClicked(TFHandle*)), this, SLOT(on_TfHandle_doubleClicked(TFHandle*))); connect(handle, SIGNAL(handleReleased()), this, SLOT(meshColorPreview())); //adding handle to scene _transferFunctionScene.addItem((QGraphicsItem*)handle); return handle; } //remove a TFHandle from the scene (and at logical level too) TFHandle* QualityMapperDialog::removeTfHandle(TFHandle *handle) { //no handle. Nothing to do. if ( handle == 0) return handle; //removing TF Handle item from scene _transferFunctionScene.removeItem( handle ); //removing it from TfHandles list for (int i=0; i<_transferFunctionHandles[handle->getChannel()].size(); i++) { if ( _transferFunctionHandles[handle->getChannel()][i] == handle ) { _transferFunctionHandles[handle->getChannel()].removeAt(i); break; } } //destroying joined logical key _transferFunction->getChannel(_currentTfHandle->getChannel()).removeKey(handle->getMyKey()); //disconnecting and destroying handle handle->disconnect(); delete handle; handle = 0; //no more traces of handle... //refreshing TF this->drawTransferFunction(); return handle; } //callback that manages the value change of current Handle y position void QualityMapperDialog::on_TF_view_doubleClicked(QPointF pos) { //getting channel for new handle: int channelCode = -1; // if ( _currentTfHandle != 0) // //if an handle was already selected let's use the same channel of the selected one // channelCode = _currentTfHandle->getChannel(); // else //changed by MAL 28\02\08 //let's use the more ahead channel in TF channelCode = _transferFunction->getFirstPlaneChanel(); float xPos = pos.x() - _transferFunction_info->leftBorder(); float yPos = pos.y() - _transferFunction_info->upperBorder(); TF_KEY *val = new TF_KEY(absolute2RelativeValf(xPos, _transferFunction_info->chartWidth()), absolute2RelativeValf(yPos, _transferFunction_info->chartHeight())); _transferFunction->getChannel(channelCode).addKey(val); TFHandle *newHandle = this->addTfHandle(channelCode, pos, val, ((channelCode + 1)*2.0f) + 1 ); if (_currentTfHandle) _currentTfHandle->setCurrentlSelected( false ); //updating currentTfHandle to sender _currentTfHandle = newHandle; _currentTfHandle->setCurrentlSelected( true ); //updating correct order among TF Handle objects this->updateTfHandlesOrder(newHandle->getChannel()); //refreshing TF scene this->drawTransferFunction(); this->updateXQualityLabel(_currentTfHandle->getRelativeX()); //applying preview if necessary if (ui.previewButton->isChecked()) //added by FB 07\02\08 on_applyButton_clicked(); } // Cut useless tails from equalizer histogram void QualityMapperDialog::on_clampButton_clicked() { _leftHandleWasInsideHistogram = false; _rightHandleWasInsideHistogram = false; // Calculate new Min e Max values _histogram_info->minX = _equalizer_histogram->Percentile((float)ui.clampHistogramSpinBox->value()/100.0f); _histogram_info->maxX = _equalizer_histogram->Percentile(1.0f-(float)ui.clampHistogramSpinBox->value()/100.0f); initEqualizerSpinboxes(); // reset Spinboxes and handles drawEqualizerHistogram(true, true); // redrawing histogram //applying preview if necessary if (ui.previewButton->isChecked()) //added by FB 21\03\08 on_applyButton_clicked(); } //writes in the x-quality label the quality value corresponding to the position of the currently selected TF handle void QualityMapperDialog::updateXQualityLabel(float xPos) { // exp = log_0.5 (_equalizerMidHandlePercentilePosition) float exp = log10((float)_equalizerMidHandlePercentilePosition) / log10(0.5f); string precision; _currentTfHandleQualityValue.setNum(relative2QualityValf(xPos, ui.minSpinBox->value(), ui.maxSpinBox->value(), exp)); if (_currentTfHandleQualityValue.size() < QUALITY_LABEL_DIGITS_NUM) { QChar paddedZeros[] = {'0','0','0','0','0','0'}; if(_currentTfHandleQualityValue.indexOf('.') == -1) _currentTfHandleQualityValue.append('.'); if ((QUALITY_LABEL_DIGITS_NUM - _currentTfHandleQualityValue.size()) > 0) _currentTfHandleQualityValue.insert(_currentTfHandleQualityValue.size(), paddedZeros, QUALITY_LABEL_DIGITS_NUM-_currentTfHandleQualityValue.size()); } ui.xQualityLabel->setText(_currentTfHandleQualityValue); } // Resets original positions and values of equalizer handles, spinboxes and brightness slider void QualityMapperDialog::setEqualizerParameters(EQUALIZER_INFO data) { // Resetting brightnessSlider position ui.brightnessSlider->setSliderPosition(data.brightness); // Resetting equalizerHistogram spinboxes values ui.minSpinBox->setValue(data.minQualityVal); ui.minSpinBox->setRange(numeric_limits::min(), data.maxQualityVal); //ui.minSpinBox->setRange(2*data.minQualityVal - data.maxQualityVal, 2*data.maxQualityVal - data.minQualityVal); ui.maxSpinBox->setValue(data.maxQualityVal); ui.maxSpinBox->setRange(data.minQualityVal, numeric_limits::max()); //ui.maxSpinBox->setRange(2*data.minQualityVal - data.maxQualityVal, 2*data.maxQualityVal - data.minQualityVal); ui.midSpinBox->setValue(((ui.maxSpinBox->value() - ui.minSpinBox->value()) * data.midQualityPercentage) + ui.minSpinBox->value()); ui.midSpinBox->setRange(ui.minSpinBox->value(), ui.maxSpinBox->value()); drawGammaCorrection(); drawTransferFunctionBG(); if (ui.previewButton->isChecked()) on_applyButton_clicked(); } void QualityMapperDialog::on_brightnessSlider_valueChanged(int value) { ui.brightessSpinBox->blockSignals(true); ui.brightessSpinBox->setValue((double)value/50.0); ui.brightessSpinBox->blockSignals(false); } void QualityMapperDialog::on_brightessSpinBox_valueChanged(double value) { ui.brightnessSlider->blockSignals(true); ui.brightnessSlider->setValue(value*50); ui.brightnessSlider->blockSignals(false); } void QualityMapperDialog::on_midSpinBox_valueChanged(double) { _signalDir = SPINBOX2LABEL; if ( _signalDir != LABEL2SPINBOX ) ui.midPercentageLine->blockSignals( true ); QString val; if ((ui.maxSpinBox->value() - ui.minSpinBox->value()) > 0) val.setNum( 100.0f * absolute2RelativeValf( ui.midSpinBox->value() - ui.minSpinBox->value(), ui.maxSpinBox->value() - ui.minSpinBox->value() ), 'g', 4 ); else val.setNum( 50.0f ); ui.midPercentageLine->setText( val ); if ( _signalDir != LABEL2SPINBOX ) ui.midPercentageLine->blockSignals( false ); /* if (ui.previewButton->isChecked()) on_applyButton_clicked(); */ _signalDir = UNKNOWN_DIRECTION; } void QualityMapperDialog::on_midPercentageLine_editingFinished() { bool conversionPossible; float numericValue = ui.midPercentageLine->text().toFloat(&conversionPossible); _signalDir = LABEL2SPINBOX; if ( _signalDir != SPINBOX2LABEL ) ui.midSpinBox->blockSignals( true ); if ((conversionPossible) && (numericValue>=0) && (numericValue<=100) ) { ui.midSpinBox->setValue( ui.minSpinBox->value() + relative2AbsoluteValf(numericValue, ui.maxSpinBox->value() - ui.minSpinBox->value()) / 100.0f ); _equalizerHandles[MID_HANDLE]->setXBySpinBoxValueChanged(ui.midSpinBox->value()); } else QMessageBox::warning(this, tr("Wrong text value"), tr("The value inserted in the text area has a bad range value or is of a not compatible type"), QMessageBox::Ok); if ( _signalDir != SPINBOX2LABEL ) ui.midSpinBox->blockSignals( false ); /* if (ui.previewButton->isChecked()) on_applyButton_clicked();*/ _signalDir = UNKNOWN_DIRECTION; }