Qucs-GUI
0.0.19
|
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 }