src/extprogs.cpp

changeset 303
8899806d382d
parent 302
08bd2c185b25
child 305
401b9e3f2d10
equal deleted inserted replaced
302:08bd2c185b25 303:8899806d382d
33 #include "file.h" 33 #include "file.h"
34 #include "widgets.h" 34 #include "widgets.h"
35 #include "history.h" 35 #include "history.h"
36 #include "labeledwidget.h" 36 #include "labeledwidget.h"
37 37
38 #include "ui_intersector.h"
39 #include "ui_coverer.h"
38 #include "ui_isecalc.h" 40 #include "ui_isecalc.h"
39 #include "ui_edger2.h" 41 #include "ui_edger2.h"
40 42
41 // ============================================================================= 43 // =============================================================================
42 cfg (str, prog_isecalc, ""); 44 cfg (str, prog_isecalc, "");
85 "tab to define a path for %1.", name)); 87 "tab to define a path for %1.", name));
86 return false; 88 return false;
87 } 89 }
88 90
89 // ============================================================================= 91 // =============================================================================
90 static void processError (const extprog prog, QProcess& proc) { 92 static str processErrorString (const extprog prog, QProcess& proc) {
91 const char* name = g_extProgNames[prog]; 93 switch (proc.error()) {
92 str errmsg;
93
94 switch (proc.error ()) {
95 case QProcess::FailedToStart: 94 case QProcess::FailedToStart:
96 errmsg = fmt ("Failed to launch %1. Check that you have set the proper path " 95 return "Failed to start (check your permissions)";
97 "to %1 and that you have the proper permissions to launch it.", name);
98 break;
99 96
100 case QProcess::Crashed: 97 case QProcess::Crashed:
101 errmsg = fmt ("%1 crashed.", name); 98 return "Crashed.";
102 break;
103 99
104 case QProcess::WriteError: 100 case QProcess::WriteError:
105 case QProcess::ReadError: 101 case QProcess::ReadError:
106 errmsg = fmt ("I/O error while interacting with %1.", name); 102 return "I/O error.";
107 break;
108 103
109 case QProcess::UnknownError: 104 case QProcess::UnknownError:
110 errmsg = fmt ("Unknown error occurred while executing %1.", name); 105 return "Unknown error";
111 break;
112 106
113 case QProcess::Timedout: 107 case QProcess::Timedout:
114 errmsg = fmt ("%1 timed out.", name); 108 return fmt( "Timed out (30 seconds)" );
115 break; 109 }
116 } 110
117 111 return "";
118 critical (errmsg);
119 } 112 }
120 113
121 // ============================================================================= 114 // =============================================================================
122 static bool mkTempFile (QTemporaryFile& tmp, str& fname) { 115 static bool mkTempFile (QTemporaryFile& tmp, str& fname) {
123 if (!tmp.open ()) 116 if (!tmp.open ())
176 } 169 }
177 170
178 writeObjects (objects, fname); 171 writeObjects (objects, fname);
179 } 172 }
180 173
181 // ============================================================================= 174 void waitForProcess( QProcess* proc ) {
182 void runUtilityProcess (extprog prog, str path, QString argvstr) { 175 proc->waitForFinished();
176
177 #if 0
178 int msecs = 30000;
179 int msectic = 10;
180
181 for (int i = 0; i < msecs / msectic; ++i) {
182 if (proc->waitForFinished (msectic))
183 return;
184
185
186 }
187 #endif // 0
188 }
189
190 // =============================================================================
191 bool runUtilityProcess (extprog prog, str path, str argvstr) {
183 QTemporaryFile input, output; 192 QTemporaryFile input, output;
184 str inputname, outputname; 193 str inputname, outputname;
185 QStringList argv = argvstr.split (" ", QString::SkipEmptyParts); 194 QStringList argv = argvstr.split (" ", QString::SkipEmptyParts);
186 195
187 print ("cmdline: %1 %2\n", path, argvstr);
188
189 #ifndef _WIN32 196 #ifndef _WIN32
190 if (g_extProgWine[prog]) { 197 if (*g_extProgWine[prog]) {
191 argv.insert (0, path); 198 argv.insert (0, path);
192 path = "wine"; 199 path = "wine";
193 } 200 }
194 #endif // _WIN32 201 #endif // _WIN32
195 202
196 if (!mkTempFile (input, inputname) || !mkTempFile (output, outputname)) 203 print ("cmdline: %1 %2\n", path, argv.join (" "));
197 return; 204
205 // Temporary files for stdin and stdout
206 if( !mkTempFile( input, inputname ) || !mkTempFile( output, outputname ))
207 return false;
198 208
199 QProcess proc; 209 QProcess proc;
200 210
201 // Init stdin 211 // Init stdin
202 File stdinfp (inputname, File::Write); 212 File stdinfp (inputname, File::Write);
203 213
204 // Begin! 214 // Begin!
205 proc.setStandardInputFile (inputname); 215 proc.setStandardInputFile (inputname);
206 proc.start (path, argv); 216 proc.start (path, argv);
207 217
208 // Write an enter - one is expected 218 // Write an enter, the utility tools all expect one
209 stdinfp.write ("\n"); 219 stdinfp.write ("\n");
210 220
211 // Wait while it runs 221 // Wait while it runs
212 proc.waitForFinished (); 222 waitForProcess( &proc );
213 223
214 #ifndef RELEASE 224 #ifndef RELEASE
215 printf ("%s", qchars (QString (proc.readAllStandardOutput ()))); 225 print ("%1", str (proc.readAllStandardOutput ()));
216 #endif // RELEASE 226 #endif // RELEASE
217 227
218 if (proc.exitStatus () == QProcess::CrashExit) { 228 str err = "";
219 processError (prog, proc); 229
220 return; 230 if ( proc.exitStatus() != QProcess::NormalExit )
221 } 231 err = processErrorString (prog, proc);
232
233 // Check the return code
234 if (proc.exitCode() != 0)
235 err = fmt ("Program exited abnormally (return code %1).", proc.exitCode());
236
237 if (err.length() > 0) {
238 critical (fmt ("%1 failed: %2\n", g_extProgNames[prog], err));
239 return false;
240 }
241
242 return true;
222 } 243 }
223 244
224 // ================================================================================================ 245 // ================================================================================================
225 static void insertOutput (str fname, bool replace, vector<short> colorsToReplace) { 246 static void insertOutput (str fname, bool replace, vector<short> colorsToReplace) {
226 #ifndef RELEASE 247 #ifndef RELEASE
316 inDATName, 337 inDATName,
317 outDATName 338 outDATName
318 }); 339 });
319 340
320 writeSelection (inDATName); 341 writeSelection (inDATName);
321 runUtilityProcess (Ytruder, prog_ytruder, argv); 342
343 if (!runUtilityProcess (Ytruder, prog_ytruder, argv))
344 return;
345
322 insertOutput (outDATName, false, {}); 346 insertOutput (outDATName, false, {});
323 } 347 }
324 348
325 // ============================================================================= 349 // =============================================================================
326 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 350 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
381 inDATName, 405 inDATName,
382 outDATName 406 outDATName
383 }); 407 });
384 408
385 writeSelection (inDATName); 409 writeSelection (inDATName);
386 runUtilityProcess (Rectifier, prog_rectifier, argv); 410
411 if (!runUtilityProcess (Rectifier, prog_rectifier, argv))
412 return;
413
387 insertOutput (outDATName, true, {}); 414 insertOutput (outDATName, true, {});
388 } 415 }
389 416
390 LabeledWidget<QComboBox>* buildColorSelector (const char* label) { 417 LabeledWidget<QComboBox>* buildColorSelector (const char* label) {
391 LabeledWidget<QComboBox>* widget = new LabeledWidget<QComboBox> (label, new QComboBox); 418 LabeledWidget<QComboBox>* widget = new LabeledWidget<QComboBox> (label, new QComboBox);
401 setlocale (LC_ALL, "C"); 428 setlocale (LC_ALL, "C");
402 429
403 if (!checkProgPath (prog_intersector, Intersector)) 430 if (!checkProgPath (prog_intersector, Intersector))
404 return; 431 return;
405 432
406 QDialog dlg; 433 QDialog* dlg = new QDialog;
407 434 Ui::IntersectorUI ui;
408 LabeledWidget<QComboBox>* cmb_incol = buildColorSelector ("Input"), 435 ui.setupUi( dlg );
409 *cmb_cutcol = buildColorSelector ("Cutter"); 436
410 QCheckBox* cb_colorize = new QCheckBox ("Colorize output"), 437 makeColorSelector( ui.cmb_incol );
411 *cb_nocondense = new QCheckBox ("No condensing"), 438 makeColorSelector( ui.cmb_cutcol );
412 *cb_repeatInverse = new QCheckBox ("Repeat inverse"), 439 ui.cb_repeat->setWhatsThis( "If this is set, " APPNAME " runs Intersector a second time with inverse files to cut the "
413 *cb_edges = new QCheckBox ("Add edges"); 440 " cutter group with the input group. Both groups are cut by the intersection." );
414 LabeledWidget<QDoubleSpinBox>* dsb_prescale = new LabeledWidget<QDoubleSpinBox> ("Prescaling factor"); 441 ui.cb_edges->setWhatsThis( "Makes " APPNAME " try run Isecalc to create edgelines for the intersection." );
415 442
416 cb_repeatInverse->setWhatsThis ("If this is set, " APPNAME " runs Intersector a second time with inverse files to cut the " 443 short inCol, cutCol;
417 " cutter group with the input group. Both groups are cut by the intersection."); 444 const bool repeatInverse = ui.cb_repeat->isChecked ();
418 cb_edges->setWhatsThis ("Makes " APPNAME " try run Isecalc to create edgelines for the intersection."); 445
419 446 for( ;; )
420 dsb_prescale->w ()->setMinimum (0.0f); 447 {
421 dsb_prescale->w ()->setMaximum (10000.0f); 448 if (!dlg->exec ())
422 dsb_prescale->w ()->setSingleStep (0.01f); 449 return;
423 dsb_prescale->w ()->setValue (1.0f); 450
424 451 inCol = ui.cmb_incol->itemData (ui.cmb_incol->currentIndex ()).toInt ();
425 QVBoxLayout* layout = new QVBoxLayout (&dlg); 452 cutCol = ui.cmb_cutcol->itemData (ui.cmb_cutcol->currentIndex ()).toInt ();
426 layout->addWidget (cmb_incol); 453
427 layout->addWidget (cmb_cutcol); 454 if (inCol == cutCol) {
428 455 critical ("Cannot use the same color group for both input and cutter!");
429 QHBoxLayout* cblayout = new QHBoxLayout; 456 continue;
430 cblayout->addWidget (cb_colorize); 457 }
431 cblayout->addWidget (cb_nocondense); 458
432 459 break;
433 QHBoxLayout* cb2layout = new QHBoxLayout;
434 cb2layout->addWidget (cb_repeatInverse);
435 cb2layout->addWidget (cb_edges);
436
437 layout->addLayout (cblayout);
438 layout->addLayout (cb2layout);
439 layout->addWidget (dsb_prescale);
440 layout->addWidget (makeButtonBox (dlg));
441
442 exec:
443 if (!dlg.exec ())
444 return;
445
446 const short inCol = cmb_incol->w ()->itemData (cmb_incol->w ()->currentIndex ()).toInt (),
447 cutCol = cmb_cutcol->w ()->itemData (cmb_cutcol->w ()->currentIndex ()).toInt ();
448 const bool repeatInverse = cb_repeatInverse->isChecked ();
449
450 if (inCol == cutCol) {
451 critical ("Cannot use the same color group for both input and cutter!");
452 goto exec;
453 } 460 }
454 461
455 // Five temporary files! 462 // Five temporary files!
456 // indat = input group file 463 // indat = input group file
457 // cutdat = cutter group file 464 // cutdat = cutter group file
467 { 474 {
468 return; 475 return;
469 } 476 }
470 477
471 str parms = join ({ 478 str parms = join ({
472 (cb_colorize->isChecked ()) ? "-c" : "", 479 (ui.cb_colorize->isChecked ()) ? "-c" : "",
473 (cb_nocondense->isChecked ()) ? "-t" : "", 480 (ui.cb_nocondense->isChecked ()) ? "-t" : "",
474 "-s", 481 "-s",
475 dsb_prescale->w ()->value () 482 ui.dsb_prescale->value ()
476 }); 483 });
477 484
478 str argv_normal = join ({ 485 str argv_normal = join ({
479 parms, 486 parms,
480 inDATName, 487 inDATName,
489 outDAT2Name 496 outDAT2Name
490 }); 497 });
491 498
492 writeColorGroup (inCol, inDATName); 499 writeColorGroup (inCol, inDATName);
493 writeColorGroup (cutCol, cutDATName); 500 writeColorGroup (cutCol, cutDATName);
494 runUtilityProcess (Intersector, prog_intersector, argv_normal); 501
502 if (!runUtilityProcess (Intersector, prog_intersector, argv_normal))
503 return;
504
495 insertOutput (outDATName, false, {inCol}); 505 insertOutput (outDATName, false, {inCol});
496 506
497 if (repeatInverse) { 507 if (repeatInverse && runUtilityProcess (Intersector, prog_intersector, argv_inverse))
498 runUtilityProcess (Intersector, prog_intersector, argv_inverse);
499 insertOutput (outDAT2Name, false, {cutCol}); 508 insertOutput (outDAT2Name, false, {cutCol});
500 } 509
501 510 if (ui.cb_edges->isChecked () && runUtilityProcess (Isecalc, prog_isecalc,
502 if (cb_edges->isChecked ()) { 511 join ({inDATName, cutDATName, edgesDATName})))
503 runUtilityProcess (Isecalc, prog_isecalc, join ({inDATName, cutDATName, edgesDATName})); 512 {
504 insertOutput (edgesDATName, false, {}); 513 insertOutput (edgesDATName, false, {});
505 } 514 }
506 } 515 }
507 516
508 // ============================================================================= 517 // =============================================================================
512 setlocale (LC_ALL, "C"); 521 setlocale (LC_ALL, "C");
513 522
514 if (!checkProgPath (prog_coverer, Coverer)) 523 if (!checkProgPath (prog_coverer, Coverer))
515 return; 524 return;
516 525
517 QDialog dlg; 526 QDialog* dlg = new QDialog;
518 527 Ui::CovererUI ui;
519 LabeledWidget<QComboBox>* cmb_col1 = buildColorSelector ("Shape 1"), 528 ui.setupUi( dlg );
520 *cmb_col2 = buildColorSelector ("Shape 2"); 529 makeColorSelector( ui.cmb_col1 );
521 530 makeColorSelector( ui.cmb_col2 );
522 QDoubleSpinBox* dsb_segsplit = new QDoubleSpinBox; 531
523 QLabel* lb_segsplit = new QLabel ("Segment split length:"); 532 short in1Col, in2Col;
524 QSpinBox* sb_bias = new QSpinBox; 533 for (;;) {
525 QLabel* lb_bias = new QLabel ("Bias:"); 534 if (!dlg->exec ())
526 QCheckBox* cb_reverse = new QCheckBox ("Reverse shape 2"), 535 return;
527 *cb_oldsweep = new QCheckBox ("Old sweep method"); 536
528 537 in1Col = ui.cmb_col1->itemData (ui.cmb_col1->currentIndex ()).toInt ();
529 dsb_segsplit->setSpecialValueText ("No splitting"); 538 in2Col = ui.cmb_col2->itemData (ui.cmb_col2->currentIndex ()).toInt ();
530 dsb_segsplit->setRange (0.0f, 10000.0f); 539
531 sb_bias->setSpecialValueText ("No bias"); 540 if (in1Col == in2Col) {
532 sb_bias->setRange (-100, 100); 541 critical ("Cannot use the same color group for both input and cutter!");
533 542 continue;
534 QGridLayout* spinboxlayout = new QGridLayout; 543 }
535 spinboxlayout->addWidget (lb_segsplit, 0, 0); 544
536 spinboxlayout->addWidget (dsb_segsplit, 0, 1); 545 break;
537 spinboxlayout->addWidget (lb_bias, 1, 0);
538 spinboxlayout->addWidget (sb_bias, 1, 1);
539
540 QVBoxLayout* layout = new QVBoxLayout (&dlg);
541 layout->addWidget (cmb_col1);
542 layout->addWidget (cmb_col2);
543 layout->addLayout (spinboxlayout);
544 layout->addWidget (cb_reverse);
545 layout->addWidget (cb_oldsweep);
546 layout->addWidget (makeButtonBox (dlg));
547
548 exec:
549 if (!dlg.exec ())
550 return;
551
552 const short in1Col = cmb_col1->w ()->itemData (cmb_col1->w ()->currentIndex ()).toInt (),
553 in2Col = cmb_col1->w ()->itemData (cmb_col2->w ()->currentIndex ()).toInt ();
554
555 if (in1Col == in2Col) {
556 critical ("Cannot use the same color group for both input and cutter!");
557 goto exec;
558 } 546 }
559 547
560 QTemporaryFile in1dat, in2dat, outdat; 548 QTemporaryFile in1dat, in2dat, outdat;
561 str in1DATName, in2DATName, outDATName; 549 str in1DATName, in2DATName, outDATName;
562 550
563 if (!mkTempFile (in1dat, in1DATName) || !mkTempFile (in2dat, in2DATName) || !mkTempFile (outdat, outDATName)) 551 if (!mkTempFile (in1dat, in1DATName) || !mkTempFile (in2dat, in2DATName) || !mkTempFile (outdat, outDATName))
564 return; 552 return;
565 553
566 str argv = join ({ 554 str argv = join ({
567 (cb_oldsweep->isChecked () ? "-s" : ""), 555 (ui.cb_oldsweep->isChecked () ? "-s" : ""),
568 (cb_reverse->isChecked () ? "-r" : ""), 556 (ui.cb_reverse->isChecked () ? "-r" : ""),
569 (dsb_segsplit->value () != 0 ? fmt ("-l %1", dsb_segsplit->value ()) : ""), 557 (ui.dsb_segsplit->value () != 0 ? fmt ("-l %1", ui.dsb_segsplit->value ()) : ""),
570 (sb_bias->value () != 0 ? fmt ("-s %1", sb_bias->value ()) : ""), 558 (ui.sb_bias->value () != 0 ? fmt ("-s %1", ui.sb_bias->value ()) : ""),
571 in1DATName, 559 in1DATName,
572 in2DATName, 560 in2DATName,
573 outDATName 561 outDATName
574 }); 562 });
575 563
576 writeColorGroup (in1Col, in1DATName); 564 writeColorGroup (in1Col, in1DATName);
577 writeColorGroup (in2Col, in2DATName); 565 writeColorGroup (in2Col, in2DATName);
578 runUtilityProcess (Coverer, prog_coverer, argv); 566
567 if (!runUtilityProcess (Coverer, prog_coverer, argv))
568 return;
569
579 insertOutput (outDATName, false, {}); 570 insertOutput (outDATName, false, {});
580 } 571 }
581 572
582 // ============================================================================= 573 // =============================================================================
583 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 574 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

mercurial