qucs_s/qucs/dialogs/tuner.cpp
Anton Midyukov e317aa50dc Improvement of internationalization
Signed-off-by: Anton Midyukov <antohami@altlinux.org>
2025-02-21 12:13:38 +03:00

918 lines
30 KiB
C++

/***************************************************************************
tuner.cpp
-----------------
begin : Sat April 11 2015
copyright : (C) 2015 by Kevin Voet
email : kevin.voet@gmail.com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "tuner.h"
#include "main.h"
#include "schematic.h"
#include "extsimkernels/spicecompat.h"
#include <QCloseEvent>
bool isPropertyTunable(Component* propertyOwner, Property* property) {
// Simulation parameters
if (propertyOwner->Model.startsWith('.')) {
return false;
}
// Properties defined in the integer+ domain
// Port number, Branches parameter in the EDD, Number of ports in a SNP file
// component
if (auto n{property->Name}; n == "Num" || n == "Branches" || n == "Ports") {
return false;
}
if (!property->Value.at(0).isNumber()) {
return false; // String
}
// Check if the value contains symbols *, /, -, +
for (const auto& chr : property->Value) {
if (!chr.isLetterOrNumber() && (chr != '.') && (chr != ' ')) {
return false;
}
if (chr.toLower() == 'e') {
break; // Scientific notation
}
}
return true;
}
tunerElement::tunerElement(QWidget *parent, Component *component, Property *pp, int selectedPropertyId)
: QWidget(parent), c(component)
{
QStringList ScaleFactorList;
ScaleFactorList << "f" << "p" << "n" << "u" << "m" << "" << "k" << "M" << "G";
int magnitudeIndex = 5;//No scaling
float minValueValidator = PTRDIFF_MIN;
bool has_unit = true;
// ************************************** HANDLE PROPERTY VALUE **************************************
//First of all we need to check whether the property is tunable (number + units) or not (random string)
//There may be an arbitrary number of spaces between magnitude and unit, so in first place we need to normalize that
//In this sense, all blank spaces are removed
QString val = pp->Value;
unit.clear();
if (val.contains("e", Qt::CaseInsensitive))//Scientific notation
{
val = misc::num2str(val.toDouble());
has_unit = false; //num2str doesn't return the unit
}
val.remove(" ");
//Now, it's time to look for the suffix... if any!
int units_index = -1;
for (int i = 0; i < val.length(); i++)
{
if (val.at(i).isLetter())
{
units_index = i;//Select index
numValue = val.mid(0, i).toFloat();//Get the magnitude
break;
}
}
if (units_index != -1)
{
//Find the units from the string
unit = val.mid(units_index);// unit should be sth like mA, mm, uV, nA, etc... here
SeparateMagnitudeFromSuffix(unit, magnitudeIndex);
}
else
{//The property value contains only numbers
numValue = val.toFloat();
has_unit = false;
}
if ((unit.length() <= 1) && (!pp->Description.isEmpty()))
{//It comes with no units... so let's try to find a suitable unit
QMap <QString, QStringList> Keywords;//Map containing the keywords that may appear in the property description
Keywords["F"] << "capacitance";
Keywords["H"] << "inductance";
Keywords["Ohm"] << "resistance";
Keywords["V"] << "voltage";
Keywords["A"] << "current";
Keywords["m"] << "length" << "width" << "thickness";
Keywords["s"] << "time";
//If one of the keywords appears in the description text, then the key of the map
//is used as unit...
bool found = false;
for(auto e : Keywords.keys())
{
for (int i = 0; i < Keywords[e].length(); i++)
{
if (pp->Description.indexOf(Keywords[e].at(i), Qt::CaseInsensitive) != -1)
{
unit = e;
found = true;
if ((i!=3) && (i !=4 ))//Not a voltage nor a current
minValueValidator = 0;//Allow positive numbers only in the lineedits
break;
}
}
if (found) break;
}
has_unit = found;
}
else
{//The value comes with unit
if (magnitudeIndex != 5) unit = unit.mid(1);
has_unit = true;
}
//Finally, add the unit to the QStringList for adding it to the scale comboboxes later
if (has_unit) for (int i = 0; i < ScaleFactorList.length(); i++) ScaleFactorList[i] += unit;
originalValue = QString::number(numValue)+ScaleFactorList[magnitudeIndex];
//****************************** HANDLE PROPERTY VALUE (END) ***************************
//UI setup
setAttribute(Qt::WA_DeleteOnClose);//This attribute forces the widget to be destroyed after closing
prop = component->Props.at(selectedPropertyId);
QGridLayout *gbox = new QGridLayout();
setLayout(gbox);
QLabel *tunerName = new QLabel(component->Name + ":" + prop->Name);
tunerName->setStyleSheet("QLabel {font: bold}");
gbox->addWidget(tunerName,0,0,1,2);
// This is locale for QDoubleValidators in QLineEdit used for editing
// max, min, current and step tuner values. We use special locale
// because we don't want QLineEdit to accept numbers in user's native
// locale, we want numbers only in "C" form: with dot as separator
// of integral and fractional part and no group separators.
//
// See also discussion here https://github.com/ra3xdh/qucs_s/issues/416
auto cDoubleLocale{ QLocale::c() };
auto opts = cDoubleLocale.numberOptions();
opts.setFlag(QLocale::RejectGroupSeparator);
cDoubleLocale.setNumberOptions(opts);
QLabel * maxLabel = new QLabel(tr("Max.:"));
maxLabel->setLineWidth(5);
gbox->addWidget(maxLabel, 1, 0);
maximum = new QLineEdit();
auto* maximumValidator = new QDoubleValidator(minValueValidator, PTRDIFF_MAX, 2, this);
maximumValidator->setLocale(cDoubleLocale);
maximum->setValidator(maximumValidator);//Prevent the user from entering text
MaxUnitsCombobox = new QComboBox(this);
MaxUnitsCombobox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
MaxUnitsCombobox->addItems(ScaleFactorList);
MaxUnitsCombobox->setCurrentIndex(magnitudeIndex);
gbox->addWidget(maximum, 1, 1);
gbox->addWidget(MaxUnitsCombobox,1,2);
slider = new QSlider(Qt::Vertical);
gbox->addWidget(slider, 2, 1, 2, 1, Qt::AlignCenter);
slider->setMinimumHeight(200);
QLabel * minLabel = new QLabel(tr("Min.:"));
minLabel->setLineWidth(5);
gbox->addWidget(minLabel, 4, 0);
minimum = new QLineEdit();
auto* minumumValidator = new QDoubleValidator(minValueValidator, 100, 2, this);
minumumValidator->setLocale(cDoubleLocale);
minimum->setValidator(minumumValidator);//Prevent the user from entering text
MinUnitsCombobox = new QComboBox(this);
MinUnitsCombobox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
MinUnitsCombobox->addItems(ScaleFactorList);
MinUnitsCombobox->setCurrentIndex(magnitudeIndex);
gbox->addWidget(minimum, 4, 1);
gbox->addWidget(MinUnitsCombobox,4,2);
QLabel *valLabel = new QLabel(tr("Val.:"));
valLabel->setLineWidth(5);
gbox->addWidget(valLabel, 5, 0);
value = new QLineEdit();
auto* valueValidator = new QDoubleValidator(minValueValidator, PTRDIFF_MAX, 2, this);
valueValidator->setLocale(cDoubleLocale);
value->setValidator(valueValidator);//Prevent the user from entering text
ValueUnitsCombobox = new QComboBox(this);
gbox->addWidget(value, 5, 1);
gbox->addWidget(ValueUnitsCombobox,5,2);
ValueUnitsCombobox->addItems(ScaleFactorList);
ValueUnitsCombobox->setCurrentIndex(magnitudeIndex);
QLabel * stepLabel = new QLabel(tr("Step"));
stepLabel->setLineWidth(5);
gbox->addWidget(stepLabel, 6, 0);
step = new QLineEdit();
auto* stepValidator = new QDoubleValidator(0, PTRDIFF_MAX, 2, this);
stepValidator->setLocale(cDoubleLocale);
step->setValidator(stepValidator);//Prevent the user from entering text
StepUnitsCombobox = new QComboBox(this);
gbox->addWidget(step, 6, 1);
gbox->addWidget(StepUnitsCombobox,6,2);
StepUnitsCombobox->addItems(ScaleFactorList);
StepUnitsCombobox->setCurrentIndex(magnitudeIndex);
QPushButton *remove = new QPushButton("", this);
gbox->addWidget(remove, 0, 2);
remove->setIcon(QIcon(":/bitmaps/svg/editdelete.svg"));
Up_Down_Buttons_Widget = new QWidget();
QGridLayout *buttonsLayout = new QGridLayout();
up = new QToolButton(this);
up->setArrowType(Qt::UpArrow);
buttonsLayout->addWidget(up, 0, 0);
down = new QToolButton(this);
down->setArrowType(Qt::DownArrow);
buttonsLayout->addWidget(down, 1, 0);
Up_Down_Buttons_Widget->setLayout(buttonsLayout);
Up_Down_Buttons_Widget->setStyleSheet("QWidget {border: 1px solid black;}");
gbox->addWidget(Up_Down_Buttons_Widget, 2, 2, 2, 1);
maxValue = numValue*1.15;// max = initial_value + 15%
minValue = numValue*0.85;// min = initial_value - 15%
stepValue = (maxValue-minValue)/20;//20 steps between minimum and maximum
maximum->setText(QString::number(maxValue));
minimum->setText(QString::number(minValue));
value->setText(QString::number(numValue));
step->setText(QString::number(stepValue));
slider->setRange(0,100);
float v = (numValue - minValue) / (maxValue - minValue);
slider->setValue(v*100);
slider->setTickPosition(QSlider::TicksBothSides);
slider->setTickInterval(0);
connect(maximum, SIGNAL(editingFinished()), this, SLOT(slotMaxValueChanged()));
connect(MaxUnitsCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotMaxValueChanged()));
connect(minimum, SIGNAL(editingFinished()), this, SLOT(slotMinValueChanged()));
connect(MinUnitsCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotMinValueChanged()));
connect(step, SIGNAL(editingFinished()), this, SLOT(slotStepChanged()));
connect(StepUnitsCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotStepChanged()));
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slotSliderChanged()));
connect(value, SIGNAL(editingFinished()), this, SLOT(slotValueChanged()));
connect(ValueUnitsCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotValueChanged()));
connect(remove, SIGNAL(released()), this, SLOT(slotDelete()));
connect(up, SIGNAL(clicked()), this, SLOT(slotUpClicked()));
connect(down, SIGNAL(clicked()), this, SLOT(slotDownClicked()));
//So far, minimum, maximum, stepValue and value has the same scale. Let's set the right value
bool aux;
maxValue = getMaxValue(aux);
minValue = getMinValue(aux);
numValue = getValue(aux);
stepValue = getStep(aux);
}
Property* tunerElement::getElementProperty()
{
return prop;
}
void tunerElement::resetValue()
{
prop->Value = originalValue;
//Reset value value
int index = 5;//By default, no scaling
QString val = SeparateMagnitudeFromSuffix(originalValue, index);
value->setText(val);
ValueUnitsCombobox->setCurrentIndex(index);
updateSlider();
slotValueChanged(false);
}
/*
* This function updates the value of the element for further simulation
*/
void tunerElement::updateProperty()
{
QString new_value = value->text();
QString suffix = ValueUnitsCombobox->currentText();
if (QucsSettings.DefaultSimulator != spicecompat::simQucsator &&
suffix == "M") { // Process Mega suffix
QString unit;
double num,fac;
misc::str2num(new_value + suffix, num, unit, fac);
prop->Value = QString::number(num*fac, 'g', 3);
} else {
prop->Value = new_value + suffix;
}
}
/*
Given a QString containing number + suffix (i.e. 2.5p, 3.9n), this function separates '2.5' from 'p' and gives the corresponding index to that suffix
*/
QString SeparateMagnitudeFromSuffix(QString num, int & index)
{
int sp=-1;
for (int i = 0; i < num.length(); i++)
{
if (num.at(i).isLetter())
{
sp = i;
break;
}
}
if (sp == -1) return num;//It doesn't come with a suffix
switch(num.at(sp).toLatin1()) {
case 'f': index = 0;
break;
case 'p': index = 1;
break;
case 'n': index = 2;
break;
case 'u': index = 3;
break;
case 'm': index = 4;
break;
case 'k': index = 6;
break;
case 'M': index = 7;
break;
case 'G': index = 8;
break;
}
return num.mid(0,sp);
}
/*
* The control reaches this function when either the maximum value QLineedit or its corresponding combobox are edited
*/
void tunerElement::slotMaxValueChanged()
{
bool ok;
float v = getMaxValue(ok);
maximum->blockSignals(true);
MaxUnitsCombobox->blockSignals(true);
if (!ok || (v <= minValue))
{
QMessageBox::warning(this, "ERROR", "Maximum value not correct", QMessageBox::Ok);
//Restore values
QString val = misc::num2str(maxValue);
int index = 5;//By default, no scaling
val = SeparateMagnitudeFromSuffix(val, index);
maximum->setText(val);
MaxUnitsCombobox->setCurrentIndex(index);
maximum->blockSignals(false);
MaxUnitsCombobox->blockSignals(false);
return;
}
maxValue = v;
if (v < numValue) {
maxValue = numValue;
//Update text... since maxValue was set to numValue and differs from what the user entered
QString val = misc::num2str(maxValue);
int index = 5;//By default, no scaling
val = SeparateMagnitudeFromSuffix(val, index);
maximum->setText(val);
MaxUnitsCombobox->setCurrentIndex(index);
}
qDebug() << "tunerElement::slotMaxValueChanged() " << v;
updateSlider();
maximum->blockSignals(false);
MaxUnitsCombobox->blockSignals(false);
}
/*
* The control reaches this function when either the minimum value QLineedit or its corresponding combobox are edited
*/
void tunerElement::slotMinValueChanged()
{
bool ok;
float v = getMinValue(ok);
minimum->blockSignals(true);
MinUnitsCombobox->blockSignals(true);
if (!ok || (v >= maxValue))
{
QMessageBox::warning(this, "ERROR", "Minimum value not correct", QMessageBox::Ok);
//Restore values
QString val = misc::num2str(minValue);
int index = 5;//By default, no scaling
val = SeparateMagnitudeFromSuffix(val, index);
minimum->setText(val);
MinUnitsCombobox->setCurrentIndex(index);
minimum->blockSignals(false);
MinUnitsCombobox->blockSignals(false);
return;
}
minValue = v;
if (v > numValue) {
minValue = numValue;
//Update text... since minValue was set to numValue and differs from what the user entered
QString val = misc::num2str(minValue);
int index = 5;//By default, no scaling
val = SeparateMagnitudeFromSuffix(val, index);
minimum->setText(val);
MinUnitsCombobox->setCurrentIndex(index);
}
qDebug() << "slotMinValueChanged() " << v;
updateSlider();
minimum->blockSignals(false);
MinUnitsCombobox->blockSignals(false);
}
/*
* The control reaches this function when either the step QLineedit or its corresponding combobox are edited
*/
void tunerElement::slotStepChanged()
{
bool ok;
float v = getStep(ok);
qDebug() << "tunerElement::slotStepChanged()" << v;
if (!ok)
{
QMessageBox::warning(this, tr("ERROR"), tr("Entered step is not correct"), QMessageBox::Ok);
//Restore previous step
QString val = misc::num2str(stepValue);
int index = 5;//By default, no scaling
val = SeparateMagnitudeFromSuffix(val, index);
step->setText(val);
StepUnitsCombobox->setCurrentIndex(index);
return;
}
stepValue = v;
}
/*
* The control reaches this function when the slider position (i.e. the value of the variable) is changed
*/
void tunerElement::slotSliderChanged()
{
value->blockSignals(true);
ValueUnitsCombobox->blockSignals(true);
float slider_v = minValue + ((slider->value()/100.0) * (maxValue - minValue));
if (slider_v > maxValue)
{
value->setText(QString::number(maxValue));
slider_v = maxValue;
}
else if (slider_v < minValue)
{
value->setText(QString::number(minValue));
slider_v = minValue;
}
numValue = slider_v;
//Update text & combobox. This is specially needed in case the input value exceeds the minumum and max. limits
QString val = misc::num2str(numValue);
int index = 5;//By default, no scaling
val = SeparateMagnitudeFromSuffix(val, index);
value->setText(val);
ValueUnitsCombobox->setCurrentIndex(index);
value->blockSignals(false);
ValueUnitsCombobox->blockSignals(false);
slotValueChanged();
}
/*
* The variable can be modified by moving the slider, editing the corresponding QLineedit box or changing the scale factor.
* The control reaches this function when one of the events above is triggered. It checks if the input value is correct, updates it
* and finally runs the simulation
*/
void tunerElement::slotValueChanged(bool simulate)
{
bool ok;
float v = getValue(ok);
value->blockSignals(true);
ValueUnitsCombobox->blockSignals(true);
if (!ok || (v < 0))
{
QMessageBox::warning(this, tr("ERROR"), tr("Value not correct"), QMessageBox::Ok);
//Restore values
QString val = misc::num2str(numValue);
int index = 5;//By default, no scaling
val = SeparateMagnitudeFromSuffix(val, index);
value->setText(val);
ValueUnitsCombobox->setCurrentIndex(index);
value->blockSignals(false);
ValueUnitsCombobox->blockSignals(false);
return;
}
numValue = v;
bool updateText = false;
//Force numValue to stay within [minValue, maxValue]
if (v > maxValue) numValue = maxValue, updateText = true;
if (v < minValue) numValue = minValue, updateText = true;
if (updateText)
{
//Update text... because of the lines above numValue may be set to maxValue or minValue so it may differ
//from what the user entered
QString val = misc::num2str(numValue);
int index = 5;//By default, no scaling
val = SeparateMagnitudeFromSuffix(val, index);
value->setText(val);
ValueUnitsCombobox->setCurrentIndex(index);
}
updateSlider();
updateProperty();
if (simulate) {
emit elementValueUpdated();
}
value->blockSignals(false);
ValueUnitsCombobox->blockSignals(false);
}
// Up button click
void tunerElement::slotUpClicked()
{
bool ok;
float s = getStep(ok);
qDebug() << "tunerElement::slotUpClicked() " << s;
numValue = getValue(ok) + s;
if (numValue > maxValue) numValue = getMaxValue(ok);
value->setText(QString::number(numValue/getScale(ValueUnitsCombobox->currentIndex()), 'f', 3));
updateSlider();//First, update the slider. Then update the value since this slot will take the value from the slider
slotValueChanged();
}
//Down button click
void tunerElement::slotDownClicked()
{
bool ok;
float s = getStep(ok);
qDebug() << "tunerElement::slotDownClicked() " << s;
numValue = getValue(ok) - s;
if (numValue < getMinValue(ok)) numValue = getMinValue(ok);
value->setText(QString::number(numValue/getScale(ValueUnitsCombobox->currentIndex()), 'f', 3));
updateSlider();//First, update the slider. Then update the value since this slot will take the value from the slider
slotValueChanged();
}
void tunerElement::slotDelete()
{
this->setVisible(false);
emit removeElement(this);
}
/*
The control reaches this function whenever the user changes the value-related inputs, i.e. textbox, scale combo or the slider
For this reason, some of the values given by the user may be out of range and there's a need to check that such data is correct
*/
void tunerElement::updateSlider()
{
float v = (numValue - minValue) / (maxValue - minValue);
slider->blockSignals(true);
slider->setValue(v*100);
slider->setTickInterval(100 / ((maxValue - minValue)/stepValue));
slider->blockSignals(false);
}
// Given the current index of a combobox, this function returns the scale factor according to the selection.
// Notice that this function does not belong neither tunerElement nor tunerDialog
float getScale(int index)
{
switch (index)
{
case 0: return pow(10, -15);//femto
case 1: return pow(10, -12);//pico
case 2: return pow(10 ,-9);//nano
case 3: return pow(10, -6);//micro
case 4: return pow(10,-3);//mili
case 5: return 1;
case 6: return pow(10, 3);//kilo
case 7: return pow(10, 6);//mega
case 8: return pow(10, 9);//giga
}
return 0;
}
// Reads the value from the user interface
float tunerElement::getValue(bool &ok)
{
return value->text().toFloat(&ok)*getScale(ValueUnitsCombobox->currentIndex());
}
// Reads the maximum value from the user interface
float tunerElement::getMaxValue(bool &ok)
{
return maximum->text().toFloat(&ok)*getScale(MaxUnitsCombobox->currentIndex());
}
// Reads the minimum value from the user interface
float tunerElement::getMinValue(bool &ok)
{
return minimum->text().toFloat(&ok)*getScale(MinUnitsCombobox->currentIndex());
}
// Reads the step from the user interface
float tunerElement::getStep(bool &ok)
{
return step->text().toFloat(&ok)*getScale(StepUnitsCombobox->currentIndex());
}
tunerElement::~tunerElement()
{
//dtor
}
/* \brief
* TUNER Dialog Class
*
*
* Tuner dialog contains tuner elements
* Updates property values
* Resets values
*
*/
//Main window. It contains zero or more tunerElement objects
TunerDialog::TunerDialog(QWidget *_w, QWidget *parent) :
QDialog(parent), w(_w)
{
setAttribute(Qt::WA_DeleteOnClose);//This attribute forces the widget to be destroyed after closing
qDebug() << "Tuner::TunerDialog";
this->setObjectName(tr("Tuner"));
this->setWindowTitle(tr("Tuner"));
gbox = new QGridLayout();
this->setLayout(gbox);
splitter = new QSplitter(parent);
ButtonsPanel = new QWidget();
QGridLayout * buttonsLayout = new QGridLayout();
ButtonsPanel->setLayout(buttonsLayout);
closeButton = new QPushButton(tr("Close"), this);
updateValues = new QPushButton(tr("Update Values"), this);
updateValues->setEnabled(false);//It doesn't make sense to activate it at first... only when at least a tuning element is active...
resetValues = new QPushButton(tr("Reset Values"), this);
resetValues->setEnabled(false);
//When pressing the Enter key at the Maxium lineedit focus is automatically set to the reset button leading to
//confunsing and unexpected behavior. In this sense, the lines below are a workaround. The focus is rejected for
//these buttons, so that focus can never reach them
resetValues->setFocusPolicy(Qt::NoFocus);
updateValues->setFocusPolicy(Qt::NoFocus);
closeButton->setFocusPolicy(Qt::NoFocus);
info = new QStatusBar;
buttonsLayout->addWidget(resetValues);
buttonsLayout->addWidget(updateValues);
buttonsLayout->addWidget(closeButton);
gbox->addWidget(ButtonsPanel, 0, 0, Qt::AlignTop);
gbox->addWidget(info, 1, 0, Qt::AlignBottom);
gbox->addWidget(splitter,0, 1, Qt::AlignRight);
progressBar = new QProgressBar();
progressBar->setMaximum(100);
progressBar->setVisible(false);
gbox->addWidget(progressBar, 2, 0);
info->showMessage(tr("Please select a component to tune"));
setMinimumWidth(300);//Otherwise, it won't fit the "help" text...
valuesUpdated = false;
connect(closeButton, SIGNAL(released()), this, SLOT(close()));
connect(resetValues, SIGNAL(released()),this, SLOT(slotResetValues()));
connect(updateValues, SIGNAL(released()), this, SLOT(slotUpdateValues()));
//Management of the Esc shortcut. Otherwise, it will exit the tuner and leave the toogle button activated
QShortcut *shortcut_Esc = new QShortcut(Qt::Key_Escape, this);
QObject::connect(shortcut_Esc, SIGNAL(activated()), this, SLOT(close()));
}
void TunerDialog::slotUpdateProgressBar(int value)
{
progressBar->setValue(value);
}
void TunerDialog::infoMsg(const QString msg)
{
info->clearMessage();
info->showMessage(msg);
}
bool TunerDialog::containsProperty(Property* prop)
{
if (currentProps.contains(prop)) return true;
else return false;
}
// Adds a tuner element (variable to be tuned)
void TunerDialog::addTunerElement(tunerElement *element)
{
if (!element)
return;
for(auto &el : currentElements) {
if (el->schematicName != element->schematicName) {
QMessageBox::warning(this,tr("Add component"),
tr("Adding components from different schematics is not supported!"));
return;
}
}
updateValues->setEnabled(true);
resetValues->setEnabled(true);
connect(element, SIGNAL(elementValueUpdated()), this, SLOT(slotElementValueUpdated()));
connect(element, SIGNAL(removeElement(tunerElement*)), this, SLOT(slotRemoveTunerElement(tunerElement*)));
if (!currentProps.contains(element->getElementProperty()))
{
splitter->addWidget(element);
currentProps.append(element->getElementProperty());
currentElements.append(element);
this->adjustSize();
this->update();
}
else
{
QMessageBox msgBox;
msgBox.setText("Already Found");
msgBox.setInformativeText("Already tuning this element");
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
}
info->clearMessage();//The user has already selected a component. It makes no longer sense to display the help message
}
// Remove tuner element(s) for a component
void TunerDialog::slotComponentDeleted(Component *c)
{
// remove elements related to the component
for(int i = currentElements.count()-1; i >= 0; --i) {
if (currentElements.at(i)->c == c)
slotRemoveTunerElement(currentElements.at(i));
}
}
void TunerDialog::slotRemoveTunerElement(tunerElement *e)
{
qDebug() << "Tuner::slotRemoveTunerElement()";
currentProps.removeAll(e->getElementProperty());
currentElements.removeAll(e);//This will also destroy the element in QSplitter: https://stackoverflow.com/questions/371599/how-to-remove-qwidgets-from-qsplitter
delete e;
if (currentProps.size() == 0)
{//No tuning boxes
updateValues->setEnabled(false);
resetValues->setEnabled(false);
info->showMessage("Please select a component to tune");
}
this->adjustSize();
this->update();
}
void TunerDialog::slotElementValueUpdated()
{
qDebug() << "Tuner::slotElementValueUpdated()";
progressBar->setVisible(true);
this->setEnabled(false);
switch (QucsSettings.DefaultSimulator) {
case spicecompat::simQucsator:
QucsMain->slotSimulate(w);
break;
case spicecompat::simNgspice:
case spicecompat::simXyce:
case spicecompat::simSpiceOpus:
QucsMain->slotSimulateWithSpice();
break;
default: break;
}
}
void TunerDialog::SimulationEnded()
{
qDebug() << "Tuner::SimulationEnded()";
this->setEnabled(true);
progressBar->setVisible(false);
}
bool TunerDialog::checkChanges()
{
for (int i = 0; i < currentElements.count(); i++)
{
int index=-1;
QString val = SeparateMagnitudeFromSuffix(currentElements.at(i)->originalValue, index);
float initial_val = val.toFloat()*getScale(index);
if (initial_val != currentElements.at(i)->numValue) return true;
}
return false;
}
void TunerDialog::slotResetValues()
{
qDebug() << "Tuner::slotResetValues()";
for (int i = 0; i < currentElements.count(); i++)
{
currentElements.at(i)->resetValue();
}
slotElementValueUpdated();
}
void TunerDialog::slotUpdateValues()
{
qDebug() << "Tuner::slotUpdateValues()";
for (int i = 0; i < currentElements.count(); i++)
{
qDebug() << "Tuner::slotUpdateValues()::updateProperty()";
currentElements.at(i)->updateProperty();
}
if (w != nullptr) { // Reflect changes in undo buffer
Schematic *d = dynamic_cast<Schematic*>(w);
d->setChanged(true,true);
}
this->infoMsg("Updated Schematic values");
valuesUpdated = true;
}
void TunerDialog::slotResetTunerDialog()
{
// Document closed. Reset tuner
qDebug() << "Tuner::slotResetTunerDialog()";
infoMsg("Document closed");
for (int i = 0; i < currentElements.count(); i++)
{
qDebug() << "Tuner::slotResetTunerDialog()::delete";
delete currentElements.at(i);
currentElements.removeAt(i);
}
this->update();
}
void TunerDialog::closeEvent(QCloseEvent *event)
{
QMessageBox::StandardButton msg;
if (!valuesUpdated && checkChanges())
{
msg = QMessageBox::question(this, "Update values before closing?",
"Do you want to update the component values before closing?",
QMessageBox::Yes | QMessageBox::No );
if (msg == QMessageBox::Yes)
{
qDebug() << "slotCloseClicked::User clicked yes";
slotUpdateValues();
}
else
slotResetValues();
}
this->valuesUpdated = false;
//Undo changes to mouse actions when closing tuner window
QucsMain->MousePressAction = &MouseActions::MPressSelect;
QucsMain->MouseReleaseAction = &MouseActions::MReleaseSelect;
QucsMain->tune->setChecked(false);
event->accept();
}
void TunerDialog::showEvent(QShowEvent *e)
{
Q_UNUSED(e);
for (int i = 0; i < currentElements.count(); i++)
{
if (currentElements.at(i)->getElementProperty() == nullptr)
currentElements.removeAt(i);
}
}
TunerDialog::~TunerDialog()
{
}