Qucs-GUI  0.0.19
/home/travis/build/Qucs/qucs/qucs/qucs/components/spicedialog.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                                spicedialog.cpp
00003                               -----------------
00004     begin                : Tue May 3 2005
00005     copyright            : (C) 2005 by Michael Margraf
00006     email                : michael.margraf@alumni.tu-berlin.de
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 #include "spicedialog.h"
00018 #include "spicefile.h"
00019 #include "main.h"
00020 #include "qucs.h"
00021 #include "schematic.h"
00022 
00023 #include <QLabel>
00024 #include <QHBoxLayout>
00025 #include <QVBoxLayout>
00026 #include <QGridLayout>
00027 #include <QLineEdit>
00028 #include <QValidator>
00029 #include <QFileDialog>
00030 #include <QPushButton>
00031 #include <QCheckBox>
00032 #include <QProcess>
00033 #include <QMessageBox>
00034 #include <QComboBox>
00035 #include <QTextStream>
00036 #include <QListWidget>
00037 #include <QListWidgetItem>
00038 #include <QDebug>
00039 
00040 
00041 SpiceDialog::SpiceDialog(QucsApp* App_, SpiceFile *c, Schematic *d)
00042     : QDialog(d, 0, TRUE, Qt::WDestructiveClose)
00043 {
00044   App = App_; // pointer to main application
00045 
00046   resize(400, 250);
00047   setWindowTitle(tr("Edit SPICE Component Properties"));
00048   Comp = c;
00049   Doc  = d;
00050 
00051   all = new QVBoxLayout(); // to provide neccessary size
00052   this->setLayout(all);
00053   QWidget *myParent = this;
00054 
00055   Expr.setPattern("[^\"=]+");  // valid expression for property 'edit' etc
00056   Validator = new QRegExpValidator(Expr, this);
00057   Expr.setPattern("[\\w_]+");  // valid expression for property 'NameEdit' etc
00058   ValRestrict = new QRegExpValidator(Expr, this);
00059 
00060   // ...........................................................
00061   QGridLayout *topGrid = new QGridLayout;
00062   all->addLayout(topGrid);
00063 
00064   CompNameEdit = new QLineEdit;
00065   CompNameEdit->setValidator(ValRestrict);
00066   connect(CompNameEdit, SIGNAL(returnPressed()), SLOT(slotButtOK()));
00067 
00068   topGrid->addWidget(new QLabel(tr("Name:")), 0, 0);
00069   topGrid->addWidget(CompNameEdit, 0, 1);
00070 
00071   
00072   FileEdit = new QLineEdit;
00073   FileEdit->setValidator(ValRestrict);
00074   connect(FileEdit, SIGNAL(returnPressed()), SLOT(slotButtOK()));
00075   ButtBrowse = new QPushButton(tr("Browse"));
00076   connect(ButtBrowse, SIGNAL(clicked()), SLOT(slotButtBrowse()));
00077 
00078   topGrid->addWidget(new QLabel(tr("File:")), 1,0);
00079   topGrid->addWidget(FileEdit, 1,1);
00080   topGrid->addWidget(ButtBrowse, 1,2);
00081 
00082 
00083   FileCheck = new QCheckBox(tr("show file name in schematic"), myParent);
00084   ButtEdit = new QPushButton(tr("Edit"), myParent);
00085   connect(ButtEdit, SIGNAL(clicked()), SLOT(slotButtEdit()));
00086 
00087   topGrid->addWidget(FileCheck, 2, 1);
00088   topGrid->addWidget(ButtEdit, 2, 2);
00089 
00090 
00091   SimCheck = new QCheckBox(tr("include SPICE simulations"), myParent);
00092   topGrid->addWidget(SimCheck, 3,1);
00093 
00094   QHBoxLayout *hcenter = new QHBoxLayout;
00095   topGrid->addLayout(hcenter, 4, 1);
00096 
00097   hcenter->setSpacing(5);
00098   PrepCombo = new QComboBox();
00099   PrepCombo->insertItems(0, QStringList() << "none" << "ps2sp" << "spicepp" << "spiceprm" );
00100   QLabel *PrepLabel = new QLabel(tr("preprocessor"));
00101   PrepLabel->setMargin(5);
00102   connect(PrepCombo, SIGNAL(activated(int)), SLOT(slotPrepChanged(int)));
00103 
00104   hcenter->addWidget(PrepCombo);
00105   hcenter->addWidget(PrepLabel);
00106 
00107   // ...........................................................
00108   QGridLayout *midGrid = new QGridLayout;
00109   all->addLayout(midGrid);
00110 
00111   midGrid->addWidget(new QLabel(tr("SPICE net nodes:")), 0,0);
00112   midGrid->addWidget(new QLabel(tr("Component ports:")), 0,2);
00113 
00114   NodesList = new QListWidget();
00115   connect(NodesList, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
00116           SLOT(slotAddPort(QListWidgetItem *)));
00117   PortsList = new QListWidget();
00118   connect(PortsList, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
00119           SLOT(slotRemovePort(QListWidgetItem *)));
00120   midGrid->addWidget(NodesList, 1,0);
00121   midGrid->addWidget(PortsList, 1,2);
00122 
00123   QVBoxLayout *vcenter = new QVBoxLayout;
00124   vcenter->setSpacing(5);
00125 
00126   ButtAdd = new QPushButton(tr("Add >>"));
00127   connect(ButtAdd, SIGNAL(clicked()), SLOT(slotButtAdd()));
00128   ButtRemove = new QPushButton(tr("<< Remove"));
00129   connect(ButtRemove, SIGNAL(clicked()), SLOT(slotButtRemove()));
00130 
00131   vcenter->addWidget(ButtAdd);
00132   vcenter->addWidget(ButtRemove);
00133   vcenter->addStretch();
00134   midGrid->addLayout(vcenter, 1,1);
00135 
00136   // ...........................................................
00137   QHBoxLayout *hbottom = new QHBoxLayout;
00138   hbottom->setSpacing(5);
00139 
00140   ButtOK = new QPushButton(tr("OK"));
00141   ButtApply = new QPushButton(tr("Apply"));
00142   ButtCancel = new QPushButton(tr("Cancel"));
00143   connect(ButtOK, SIGNAL(clicked()), SLOT(slotButtOK()));
00144   connect(ButtApply, SIGNAL(clicked()), SLOT(slotButtApply()));
00145   connect(ButtCancel, SIGNAL(clicked()), SLOT(slotButtCancel()));
00146   hbottom->addStretch();
00147   hbottom->addWidget(ButtOK);
00148   hbottom->addWidget(ButtApply);
00149   hbottom->addWidget(ButtCancel);
00150 
00151   all->addLayout(hbottom);
00152 
00153   // ------------------------------------------------------------
00154   CompNameEdit->setText(Comp->Name);
00155   changed = false;
00156 
00157   // insert all properties into the ListBox
00158   Property *pp = Comp->Props.first();
00159   FileEdit->setText(pp->Value);
00160   FileCheck->setChecked(pp->display);
00161   SimCheck->setChecked(Comp->Props.at(2)->Value == "yes");
00162   for(int i=0; i<PrepCombo->count(); i++)
00163   {
00164     if(PrepCombo->text(i) == Comp->Props.at(3)->Value)
00165     {
00166       PrepCombo->setCurrentItem(i);
00167       currentPrep = i;
00168       break;
00169     }
00170   }
00171 
00172   loadSpiceNetList(pp->Value);  // load netlist nodes
00173 }
00174 
00175 SpiceDialog::~SpiceDialog()
00176 {
00177   delete all;
00178   delete Validator;
00179   delete ValRestrict;
00180 }
00181 
00182 // -------------------------------------------------------------------------
00183 // Is called if the "OK"-button is pressed.
00184 void SpiceDialog::slotButtOK()
00185 {
00186   slotButtApply();
00187   slotButtCancel();
00188 }
00189 
00190 // -------------------------------------------------------------------------
00191 // Is called if the "Cancel"-button is pressed.
00192 void SpiceDialog::slotButtCancel()
00193 {
00194   if(changed) done(1);  // changed could have been done before
00195   else done(0);   // (by "Apply"-button)
00196 }
00197 
00198 //-----------------------------------------------------------------
00199 // To get really all close events (even <Escape> key).
00200 void SpiceDialog::reject()
00201 {
00202   slotButtCancel();
00203 }
00204 
00205 // -------------------------------------------------------------------------
00206 // Is called, if the "Apply"-button is pressed.
00207 void SpiceDialog::slotButtApply()
00208 {
00209   Component *pc;
00210   if(CompNameEdit->text().isEmpty())  CompNameEdit->setText(Comp->Name);
00211   else if(CompNameEdit->text() != Comp->Name)
00212   {
00213     for(pc = Doc->Components->first(); pc!=0; pc = Doc->Components->next())
00214       if(pc->Name == CompNameEdit->text()) {
00215         break;  // found component with the same name ?
00216       }
00217     if (pc) {
00218       CompNameEdit->setText(Comp->Name);
00219     }
00220     else {
00221       Comp->Name = CompNameEdit->text();
00222       changed = true;
00223     }
00224   }
00225 
00226   // apply all the new property values
00227   Property *pp = Comp->Props.first();
00228   if(pp->Value != FileEdit->text())
00229   {
00230     pp->Value = FileEdit->text();
00231     changed = true;
00232   }
00233   if(pp->display != FileCheck->isChecked())
00234   {
00235     pp->display = FileCheck->isChecked();
00236     changed = true;
00237   }
00238 
00239   QString tmp;
00240   for(int i=0; i<PortsList->count(); i++)
00241   {
00242     if (!tmp.isEmpty()) {
00243       tmp += ',';
00244     }
00245     tmp += "_net" + PortsList->item(i)->text();   // chosen ports
00246   }
00247   pp = Comp->Props.next();
00248   if(pp->Value != tmp)
00249   {
00250     pp->Value = tmp;
00251     changed = true;
00252   }
00253   pp = Comp->Props.next();
00254   if((pp->Value=="yes") != SimCheck->isChecked())
00255   {
00256     pp->Value = ((SimCheck->isChecked())? "yes" : "no");
00257     changed = true;
00258   }
00259   if(pp->Value != "yes") {
00260     Comp->withSim = false;
00261   }
00262 
00263   pp = Comp->Props.next();
00264   if(pp->Value != PrepCombo->currentText())
00265   {
00266     pp->Value = PrepCombo->currentText();
00267     changed = true;
00268   }
00269 
00270   if(changed || Comp->withSim)    // because of "sim" text
00271   {
00272     Doc->recreateComponent(Comp); // to apply changes to the schematic symbol
00273     Doc->viewport()->repaint();
00274   }
00275 }
00276 
00277 // -------------------------------------------------------------------------
00278 void SpiceDialog::slotButtBrowse()
00279 {
00280   QString s = QFileDialog::getOpenFileName(this,
00281                   tr("Select a file"),
00282                   lastDir.isEmpty() ? QString(".") : lastDir,
00283                   tr("SPICE netlist") + QString(" (") + QucsSettings.spiceExtensions.join(" ") + QString(");;")
00284                       + tr("All Files") + " (*.*)");
00285 
00286   if(s.isEmpty()) {
00287     return;
00288   }
00289 
00290   QFileInfo Info(s);
00291   lastDir = Info.dirPath(true);  // remember last directory
00292 
00293   // snip path if file in current directory
00294   if(QucsSettings.QucsWorkDir.exists(Info.fileName()) &&
00295           QucsSettings.QucsWorkDir.absPath() == Info.dirPath(true)) {
00296     s = Info.fileName();
00297   }
00298   FileEdit->setText(s);
00299 
00300   Comp->Props.at(1)->Value = "";
00301   loadSpiceNetList(s);
00302 }
00303 
00304 // -------------------------------------------------------------------------
00305 void SpiceDialog::slotPrepChanged(int i)
00306 {
00307   if(currentPrep != i)
00308   {
00309     currentPrep = i;
00310     PrepCombo->setCurrentItem(i);
00311     loadSpiceNetList(FileEdit->text());  // reload netlist nodes
00312   }
00313 }
00314 
00315 // -------------------------------------------------------------------------
00316 bool SpiceDialog::loadSpiceNetList(const QString& s)
00317 {
00318   Comp->withSim = false;
00319   if(s.isEmpty()) return false;
00320   QFileInfo FileInfo(QucsSettings.QucsWorkDir, s);
00321 
00322   NodesList->clear();
00323   PortsList->clear();
00324   textStatus = 0;
00325   Line = Error = "";
00326 
00327   QString preprocessor = PrepCombo->currentText();
00328   if (preprocessor != "none")
00329   {
00330     qDebug() << "Run spice preprocessor (perl)";
00331     bool piping = true;
00332     QString script;
00333 #ifdef __MINGW32__
00334     QString interpreter = "tinyperl.exe";
00335 #else
00336     QString interpreter = "perl";
00337 #endif
00338     if (preprocessor == "ps2sp")
00339     {
00340       script = "ps2sp";
00341     }
00342     else if (preprocessor == "spicepp")
00343     {
00344       script = "spicepp.pl";
00345     }
00346     else if (preprocessor == "spiceprm")
00347     {
00348       script = "spiceprm";
00349       piping = false;
00350     }
00351     script = QucsSettings.BinDir + script;
00352     QString spiceCommand;
00353     SpicePrep = new QProcess(this);
00354     spiceCommand+=interpreter + " ";
00355     spiceCommand+=script + " ";
00356     spiceCommand+=FileInfo.filePath() + " ";
00357 
00358     QFile PrepFile;
00359     QFileInfo PrepInfo(QucsSettings.QucsWorkDir, s + ".pre");
00360     QString PrepName = PrepInfo.filePath();
00361 
00362     if (!piping)
00363     {
00364       spiceCommand += PrepName + " ";
00365       connect(SpicePrep, SIGNAL(readyReadStandardOutput()), SLOT(slotSkipOut()));
00366       connect(SpicePrep, SIGNAL(readyReadStandardError()), SLOT(slotGetPrepErr()));
00367     }
00368     else
00369     {
00370       connect(SpicePrep, SIGNAL(readyReadStandardOutput()), SLOT(slotGetPrepOut()));
00371       connect(SpicePrep, SIGNAL(readyReadStandardError()), SLOT(slotGetPrepErr()));
00372     }
00373 
00374     QMessageBox *MBox = new QMessageBox(tr("Info"),
00375                                         tr("Preprocessing SPICE file \"%1\".").arg(FileInfo.filePath()),
00376                                         QMessageBox::NoIcon, QMessageBox::Abort,
00377                                         QMessageBox::NoButton, QMessageBox::NoButton, this, 0, true,
00378                                         Qt::WStyle_DialogBorder |  Qt::WDestructiveClose);
00379 
00380     connect(SpicePrep, SIGNAL(finished(int, QProcess::ExitStatus)), MBox, SLOT(close()));
00381 
00382     if (piping)
00383     {
00384       PrepFile.setFileName(PrepName);
00385       if(!PrepFile.open(QIODevice::WriteOnly))
00386       {
00387         QMessageBox::critical(this, tr("Error"),
00388                               tr("Cannot save preprocessed SPICE file \"%1\".").
00389                               arg(PrepName));
00390         return false;
00391       }
00392       prestream = new QTextStream(&PrepFile);
00393     }
00394     SpicePrep->start(spiceCommand);
00395     if ((SpicePrep->state() != QProcess::Starting) &&
00396         (SpicePrep->state() != QProcess::Running))
00397     {
00398         QMessageBox::critical(this, tr("Error"),
00399                               tr("Cannot execute \"%1\".").arg(interpreter + " " + script));
00400         if (piping)
00401         {
00402             PrepFile.close();
00403             delete prestream;
00404         }
00405         return false;
00406     }
00407     //SpicePrep->closeStdin();
00408 
00409     MBox->exec();
00410     delete SpicePrep;
00411     if (piping)
00412     {
00413         PrepFile.close();
00414         delete prestream;
00415     }
00416 
00417     if(!Error.isEmpty())
00418     {
00419         QMessageBox::critical(this, tr("SPICE Preprocessor Error"), Error);
00420         return false;
00421     }
00422     FileInfo = QFileInfo(QucsSettings.QucsWorkDir, s + ".pre");
00423   }
00424 
00425   // Now do the spice->qucs netlist conversion using the qucsconv program ...
00426   QucsConv = new QProcess(this);
00427 
00428   QString Program;
00429   QStringList Arguments;
00430   Program = QucsSettings.Qucsconv;
00431   Arguments << "-if" << "spice"
00432             << "-of" <<  "qucs"
00433             << "-i" << FileInfo.filePath();
00434 
00435   qDebug() << "Command :" << Program << Arguments.join(" ");
00436 
00437   connect(QucsConv, SIGNAL(readyReadStandardOutput()), SLOT(slotGetNetlist()));
00438   connect(QucsConv, SIGNAL(readyReadStandardError()), SLOT(slotGetError()));
00439 
00440 
00441   QMessageBox *MBox = new QMessageBox(tr("Info"),
00442                                       tr("Converting SPICE file \"%1\".").arg(FileInfo.filePath()),
00443                                       QMessageBox::NoIcon, QMessageBox::Abort,
00444                                       QMessageBox::NoButton, QMessageBox::NoButton, this, 0, true,
00445                                       Qt::WStyle_DialogBorder |  Qt::WDestructiveClose);
00446 
00447   connect(QucsConv, SIGNAL(finished(int, QProcess::ExitStatus)), MBox, SLOT(close()));
00448 
00449   QucsConv->start(Program, Arguments);
00450 
00451   if(!QucsConv->Running)
00452   {
00453     QMessageBox::critical(this, tr("Error"),
00454                           tr("Cannot execute \"%1\".").arg(QucsSettings.Qucsconv));
00455     return false;
00456   }
00457 
00458   MBox->exec();
00459 
00460   if (!Error.isEmpty()) {
00461       QMessageBox::critical(this, tr("QucsConv Error"), Error);
00462   }
00463 
00464   Property *pp = Comp->Props.at(1);
00465   if(!pp->Value.isEmpty())
00466   {
00467     PortsList->clear();
00468     QStringList ports = QStringList::split(',', pp->Value);
00469     foreach(QString port, ports) {
00470       PortsList->addItem(port);
00471     }
00472   }
00473 
00474   QString tmp;
00475   QList<QListWidgetItem *> plist;
00476   for(int i=0; i<PortsList->count(); i++)
00477   {
00478     tmp = PortsList->item(i)->text().remove(0, 4);
00479     PortsList->item(i)->setText(tmp);
00480 
00481     plist = NodesList->findItems(tmp, Qt::MatchCaseSensitive | Qt::MatchExactly);
00482     if (!plist.isEmpty()) {
00483       delete plist[0];
00484     } else {
00485       delete PortsList->item(i);
00486     }
00487   }
00488   return true;
00489 }
00490 
00491 // -------------------------------------------------------------------------
00492 void SpiceDialog::slotSkipErr()
00493 {
00494   SpicePrep->readAllStandardError ();
00495 }
00496 
00497 // -------------------------------------------------------------------------
00498 void SpiceDialog::slotSkipOut()
00499 {
00500   SpicePrep->readAllStandardOutput ();
00501 }
00502 
00503 // -------------------------------------------------------------------------
00504 void SpiceDialog::slotGetPrepErr()
00505 {
00506   Error += QString(SpicePrep->readAllStandardError ());
00507 }
00508 
00509 // -------------------------------------------------------------------------
00510 void SpiceDialog::slotGetPrepOut()
00511 {
00512   (*prestream) << QString(SpicePrep->readAllStandardOutput ());
00513 }
00514 
00515 // -------------------------------------------------------------------------
00516 void SpiceDialog::slotGetError()
00517 {
00518   Error += QString(QucsConv->readAllStandardError ());
00519 }
00520 
00521 // -------------------------------------------------------------------------
00522 void SpiceDialog::slotGetNetlist()
00523 {
00524   qDebug() << "slotGetNetlist";
00525   int i;
00526   QString s;
00527   Line += QString(QucsConv->readAllStandardOutput ());
00528 
00529   while((i = Line.indexOf('\n')) >= 0)
00530   {
00531     s = Line.left(i);
00532     Line.remove(0, i+1);
00533     s = s.trimmed();
00534     if (!s.isEmpty ())
00535     {
00536       if (s.at(0) == '.')
00537       {
00538         if (s.left(5) != ".Def:")
00539         {
00540           Comp->withSim = true;
00541         }
00542         continue;
00543       }
00544     }
00545 
00546     switch(textStatus)
00547     {
00548     case 0:
00549       if (s == "### TOPLEVEL NODELIST BEGIN")
00550       {
00551           textStatus = 1;
00552       }
00553       else if (s == "### SPICE OUTPUT NODELIST BEGIN")
00554       {
00555           textStatus = 2;
00556       }
00557       break;
00558 
00559     case 1:
00560       if (s == "### TOPLEVEL NODELIST END")
00561       {
00562           textStatus = 0;
00563           break;
00564       }
00565 
00566       if (s.left(2) != "# ")
00567       {
00568           break;
00569       }
00570       s.remove(0, 2);
00571 
00572       if(s.left(4) == "_net")
00573       {
00574           NodesList->addItem(s.remove(0, 4));
00575       }
00576       break;
00577 
00578     case 2:
00579       if(s == "### SPICE OUTPUT NODELIST END")
00580       {
00581           textStatus = 0;
00582           break;
00583       }
00584       if(s.left(2) != "# ")
00585       {
00586           break;
00587       }
00588       s.remove(0, 2);
00589 
00590       if(s.left(4) == "_net")
00591       {
00592           PortsList->addItem(s);   // prefix "_net" is removed later on
00593       }
00594       break;
00595     }
00596   }
00597 }
00598 
00599 // -------------------------------------------------------------------------
00600 void SpiceDialog::slotButtEdit()
00601 {
00602   Doc->App->editFile(QucsSettings.QucsWorkDir.filePath(FileEdit->text()));
00603 }
00604 
00605 // -------------------------------------------------------------------------
00606 // Is called if the add button is pressed.
00607 void SpiceDialog::slotButtAdd()
00608 {
00609   QListWidgetItem *item = NodesList->currentItem();
00610   if (item) {
00611     PortsList->addItem(item->text());
00612     delete(item);
00613   }
00614 }
00615 
00616 // -------------------------------------------------------------------------
00617 // Is called if the remove button is pressed.
00618 void SpiceDialog::slotButtRemove()
00619 {
00620   QListWidgetItem *item = PortsList->currentItem();
00621   if (item) {
00622     NodesList->addItem(item->text());
00623     delete(item);
00624   }
00625 }
00626 
00627 // -------------------------------------------------------------------------
00628 // Is called when double-click on NodesList-Box
00629 void SpiceDialog::slotAddPort(QListWidgetItem *Item)
00630 {
00631   if(Item) slotButtAdd();
00632 }
00633 
00634 // -------------------------------------------------------------------------
00635 // Is called when double-click on PortsList-Box
00636 void SpiceDialog::slotRemovePort(QListWidgetItem *Item)
00637 {
00638   if(Item) slotButtRemove();
00639 }
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines