Thu, 27 Jun 2013 12:12:53 +0300
Converted Intersector UI, improved handling of failed ext program launches, fixed launch of non-wine programs
changelog.txt | file | annotate | diff | comparison | revisions | |
src/configDialog.cpp | file | annotate | diff | comparison | revisions | |
src/extprogs.cpp | file | annotate | diff | comparison | revisions | |
src/ui/coverer.ui | file | annotate | diff | comparison | revisions |
--- a/changelog.txt Thu Jun 27 03:00:00 2013 +0300 +++ b/changelog.txt Thu Jun 27 12:12:53 2013 +0300 @@ -14,6 +14,7 @@ - Objects can now be edited by double-clicking on them. - Changed default keys for Y-axis moving from PgUp/PgDown to Home and End ala MLCAD. - [Linux] External programs can now be launched with Wine. +- Improved external program fault handling: don't try to replace/append the output in case of failure, catch non-zero exit codes. - Added a progress box for file loading to respond to desktops while loading files. With large files the no-response policy could be a bad thing. - Fixed: Recent files should behave coherently now.
--- a/src/configDialog.cpp Thu Jun 27 03:00:00 2013 +0300 +++ b/src/configDialog.cpp Thu Jun 27 12:12:53 2013 +0300 @@ -715,7 +715,10 @@ // Ext program settings for (const extProgInfo& info : g_extProgInfo) + { *info.path = info.input->text (); + *info.wine = info.wineBox->isChecked(); + } // Save the config config::save ();
--- a/src/extprogs.cpp Thu Jun 27 03:00:00 2013 +0300 +++ b/src/extprogs.cpp Thu Jun 27 12:12:53 2013 +0300 @@ -35,6 +35,8 @@ #include "history.h" #include "labeledwidget.h" +#include "ui_intersector.h" +#include "ui_coverer.h" #include "ui_isecalc.h" #include "ui_edger2.h" @@ -87,35 +89,26 @@ } // ============================================================================= -static void processError (const extprog prog, QProcess& proc) { - const char* name = g_extProgNames[prog]; - str errmsg; - - switch (proc.error ()) { +static str processErrorString (const extprog prog, QProcess& proc) { + switch (proc.error()) { case QProcess::FailedToStart: - errmsg = fmt ("Failed to launch %1. Check that you have set the proper path " - "to %1 and that you have the proper permissions to launch it.", name); - break; + return "Failed to start (check your permissions)"; case QProcess::Crashed: - errmsg = fmt ("%1 crashed.", name); - break; + return "Crashed."; case QProcess::WriteError: case QProcess::ReadError: - errmsg = fmt ("I/O error while interacting with %1.", name); - break; + return "I/O error."; case QProcess::UnknownError: - errmsg = fmt ("Unknown error occurred while executing %1.", name); - break; + return "Unknown error"; case QProcess::Timedout: - errmsg = fmt ("%1 timed out.", name); - break; + return fmt( "Timed out (30 seconds)" ); } - critical (errmsg); + return ""; } // ============================================================================= @@ -178,23 +171,40 @@ writeObjects (objects, fname); } +void waitForProcess( QProcess* proc ) { + proc->waitForFinished(); + +#if 0 + int msecs = 30000; + int msectic = 10; + + for (int i = 0; i < msecs / msectic; ++i) { + if (proc->waitForFinished (msectic)) + return; + + + } +#endif // 0 +} + // ============================================================================= -void runUtilityProcess (extprog prog, str path, QString argvstr) { +bool runUtilityProcess (extprog prog, str path, str argvstr) { QTemporaryFile input, output; str inputname, outputname; QStringList argv = argvstr.split (" ", QString::SkipEmptyParts); - print ("cmdline: %1 %2\n", path, argvstr); - #ifndef _WIN32 - if (g_extProgWine[prog]) { + if (*g_extProgWine[prog]) { argv.insert (0, path); path = "wine"; } #endif // _WIN32 - if (!mkTempFile (input, inputname) || !mkTempFile (output, outputname)) - return; + print ("cmdline: %1 %2\n", path, argv.join (" ")); + + // Temporary files for stdin and stdout + if( !mkTempFile( input, inputname ) || !mkTempFile( output, outputname )) + return false; QProcess proc; @@ -205,20 +215,31 @@ proc.setStandardInputFile (inputname); proc.start (path, argv); - // Write an enter - one is expected + // Write an enter, the utility tools all expect one stdinfp.write ("\n"); // Wait while it runs - proc.waitForFinished (); + waitForProcess( &proc ); #ifndef RELEASE - printf ("%s", qchars (QString (proc.readAllStandardOutput ()))); + print ("%1", str (proc.readAllStandardOutput ())); #endif // RELEASE - if (proc.exitStatus () == QProcess::CrashExit) { - processError (prog, proc); - return; + str err = ""; + + if ( proc.exitStatus() != QProcess::NormalExit ) + err = processErrorString (prog, proc); + + // Check the return code + if (proc.exitCode() != 0) + err = fmt ("Program exited abnormally (return code %1).", proc.exitCode()); + + if (err.length() > 0) { + critical (fmt ("%1 failed: %2\n", g_extProgNames[prog], err)); + return false; } + + return true; } // ================================================================================================ @@ -318,7 +339,10 @@ }); writeSelection (inDATName); - runUtilityProcess (Ytruder, prog_ytruder, argv); + + if (!runUtilityProcess (Ytruder, prog_ytruder, argv)) + return; + insertOutput (outDATName, false, {}); } @@ -383,7 +407,10 @@ }); writeSelection (inDATName); - runUtilityProcess (Rectifier, prog_rectifier, argv); + + if (!runUtilityProcess (Rectifier, prog_rectifier, argv)) + return; + insertOutput (outDATName, true, {}); } @@ -403,53 +430,33 @@ if (!checkProgPath (prog_intersector, Intersector)) return; - QDialog dlg; - - LabeledWidget<QComboBox>* cmb_incol = buildColorSelector ("Input"), - *cmb_cutcol = buildColorSelector ("Cutter"); - QCheckBox* cb_colorize = new QCheckBox ("Colorize output"), - *cb_nocondense = new QCheckBox ("No condensing"), - *cb_repeatInverse = new QCheckBox ("Repeat inverse"), - *cb_edges = new QCheckBox ("Add edges"); - LabeledWidget<QDoubleSpinBox>* dsb_prescale = new LabeledWidget<QDoubleSpinBox> ("Prescaling factor"); + QDialog* dlg = new QDialog; + Ui::IntersectorUI ui; + ui.setupUi( dlg ); - cb_repeatInverse->setWhatsThis ("If this is set, " APPNAME " runs Intersector a second time with inverse files to cut the " - " cutter group with the input group. Both groups are cut by the intersection."); - cb_edges->setWhatsThis ("Makes " APPNAME " try run Isecalc to create edgelines for the intersection."); + makeColorSelector( ui.cmb_incol ); + makeColorSelector( ui.cmb_cutcol ); + ui.cb_repeat->setWhatsThis( "If this is set, " APPNAME " runs Intersector a second time with inverse files to cut the " + " cutter group with the input group. Both groups are cut by the intersection." ); + ui.cb_edges->setWhatsThis( "Makes " APPNAME " try run Isecalc to create edgelines for the intersection." ); - dsb_prescale->w ()->setMinimum (0.0f); - dsb_prescale->w ()->setMaximum (10000.0f); - dsb_prescale->w ()->setSingleStep (0.01f); - dsb_prescale->w ()->setValue (1.0f); - - QVBoxLayout* layout = new QVBoxLayout (&dlg); - layout->addWidget (cmb_incol); - layout->addWidget (cmb_cutcol); + short inCol, cutCol; + const bool repeatInverse = ui.cb_repeat->isChecked (); - QHBoxLayout* cblayout = new QHBoxLayout; - cblayout->addWidget (cb_colorize); - cblayout->addWidget (cb_nocondense); - - QHBoxLayout* cb2layout = new QHBoxLayout; - cb2layout->addWidget (cb_repeatInverse); - cb2layout->addWidget (cb_edges); - - layout->addLayout (cblayout); - layout->addLayout (cb2layout); - layout->addWidget (dsb_prescale); - layout->addWidget (makeButtonBox (dlg)); - -exec: - if (!dlg.exec ()) - return; - - const short inCol = cmb_incol->w ()->itemData (cmb_incol->w ()->currentIndex ()).toInt (), - cutCol = cmb_cutcol->w ()->itemData (cmb_cutcol->w ()->currentIndex ()).toInt (); - const bool repeatInverse = cb_repeatInverse->isChecked (); - - if (inCol == cutCol) { - critical ("Cannot use the same color group for both input and cutter!"); - goto exec; + for( ;; ) + { + if (!dlg->exec ()) + return; + + inCol = ui.cmb_incol->itemData (ui.cmb_incol->currentIndex ()).toInt (); + cutCol = ui.cmb_cutcol->itemData (ui.cmb_cutcol->currentIndex ()).toInt (); + + if (inCol == cutCol) { + critical ("Cannot use the same color group for both input and cutter!"); + continue; + } + + break; } // Five temporary files! @@ -469,10 +476,10 @@ } str parms = join ({ - (cb_colorize->isChecked ()) ? "-c" : "", - (cb_nocondense->isChecked ()) ? "-t" : "", + (ui.cb_colorize->isChecked ()) ? "-c" : "", + (ui.cb_nocondense->isChecked ()) ? "-t" : "", "-s", - dsb_prescale->w ()->value () + ui.dsb_prescale->value () }); str argv_normal = join ({ @@ -491,16 +498,18 @@ writeColorGroup (inCol, inDATName); writeColorGroup (cutCol, cutDATName); - runUtilityProcess (Intersector, prog_intersector, argv_normal); + + if (!runUtilityProcess (Intersector, prog_intersector, argv_normal)) + return; + insertOutput (outDATName, false, {inCol}); - if (repeatInverse) { - runUtilityProcess (Intersector, prog_intersector, argv_inverse); + if (repeatInverse && runUtilityProcess (Intersector, prog_intersector, argv_inverse)) insertOutput (outDAT2Name, false, {cutCol}); - } - if (cb_edges->isChecked ()) { - runUtilityProcess (Isecalc, prog_isecalc, join ({inDATName, cutDATName, edgesDATName})); + if (ui.cb_edges->isChecked () && runUtilityProcess (Isecalc, prog_isecalc, + join ({inDATName, cutDATName, edgesDATName}))) + { insertOutput (edgesDATName, false, {}); } } @@ -514,47 +523,26 @@ if (!checkProgPath (prog_coverer, Coverer)) return; - QDialog dlg; - - LabeledWidget<QComboBox>* cmb_col1 = buildColorSelector ("Shape 1"), - *cmb_col2 = buildColorSelector ("Shape 2"); - - QDoubleSpinBox* dsb_segsplit = new QDoubleSpinBox; - QLabel* lb_segsplit = new QLabel ("Segment split length:"); - QSpinBox* sb_bias = new QSpinBox; - QLabel* lb_bias = new QLabel ("Bias:"); - QCheckBox* cb_reverse = new QCheckBox ("Reverse shape 2"), - *cb_oldsweep = new QCheckBox ("Old sweep method"); - - dsb_segsplit->setSpecialValueText ("No splitting"); - dsb_segsplit->setRange (0.0f, 10000.0f); - sb_bias->setSpecialValueText ("No bias"); - sb_bias->setRange (-100, 100); + QDialog* dlg = new QDialog; + Ui::CovererUI ui; + ui.setupUi( dlg ); + makeColorSelector( ui.cmb_col1 ); + makeColorSelector( ui.cmb_col2 ); - QGridLayout* spinboxlayout = new QGridLayout; - spinboxlayout->addWidget (lb_segsplit, 0, 0); - spinboxlayout->addWidget (dsb_segsplit, 0, 1); - spinboxlayout->addWidget (lb_bias, 1, 0); - spinboxlayout->addWidget (sb_bias, 1, 1); - - QVBoxLayout* layout = new QVBoxLayout (&dlg); - layout->addWidget (cmb_col1); - layout->addWidget (cmb_col2); - layout->addLayout (spinboxlayout); - layout->addWidget (cb_reverse); - layout->addWidget (cb_oldsweep); - layout->addWidget (makeButtonBox (dlg)); - -exec: - if (!dlg.exec ()) - return; - - const short in1Col = cmb_col1->w ()->itemData (cmb_col1->w ()->currentIndex ()).toInt (), - in2Col = cmb_col1->w ()->itemData (cmb_col2->w ()->currentIndex ()).toInt (); - - if (in1Col == in2Col) { - critical ("Cannot use the same color group for both input and cutter!"); - goto exec; + short in1Col, in2Col; + for (;;) { + if (!dlg->exec ()) + return; + + in1Col = ui.cmb_col1->itemData (ui.cmb_col1->currentIndex ()).toInt (); + in2Col = ui.cmb_col2->itemData (ui.cmb_col2->currentIndex ()).toInt (); + + if (in1Col == in2Col) { + critical ("Cannot use the same color group for both input and cutter!"); + continue; + } + + break; } QTemporaryFile in1dat, in2dat, outdat; @@ -564,10 +552,10 @@ return; str argv = join ({ - (cb_oldsweep->isChecked () ? "-s" : ""), - (cb_reverse->isChecked () ? "-r" : ""), - (dsb_segsplit->value () != 0 ? fmt ("-l %1", dsb_segsplit->value ()) : ""), - (sb_bias->value () != 0 ? fmt ("-s %1", sb_bias->value ()) : ""), + (ui.cb_oldsweep->isChecked () ? "-s" : ""), + (ui.cb_reverse->isChecked () ? "-r" : ""), + (ui.dsb_segsplit->value () != 0 ? fmt ("-l %1", ui.dsb_segsplit->value ()) : ""), + (ui.sb_bias->value () != 0 ? fmt ("-s %1", ui.sb_bias->value ()) : ""), in1DATName, in2DATName, outDATName @@ -575,7 +563,10 @@ writeColorGroup (in1Col, in1DATName); writeColorGroup (in2Col, in2DATName); - runUtilityProcess (Coverer, prog_coverer, argv); + + if (!runUtilityProcess (Coverer, prog_coverer, argv)) + return; + insertOutput (outDATName, false, {}); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ui/coverer.ui Thu Jun 27 12:12:53 2013 +0300 @@ -0,0 +1,157 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>CovererUI</class> + <widget class="QDialog" name="CovererUI"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>310</width> + <height>220</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>310</width> + <height>220</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>10000</width> + <height>10000</height> + </size> + </property> + <property name="windowTitle"> + <string>Coverer</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="geometry"> + <rect> + <x>40</x> + <y>180</y> + <width>261</width> + <height>32</height> + </rect> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + <widget class="QWidget" name="gridLayoutWidget"> + <property name="geometry"> + <rect> + <x>10</x> + <y>5</y> + <width>291</width> + <height>171</height> + </rect> + </property> + <layout class="QGridLayout" name="gridLayout" columnstretch="0,1"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Shape 1</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Shape 2</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="cmb_col2"/> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="cmb_col1"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Segment split length:</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Bias:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QDoubleSpinBox" name="dsb_segsplit"> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QSpinBox" name="sb_bias"> + <property name="minimum"> + <number>-100</number> + </property> + <property name="maximum"> + <number>100</number> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="cb_reverse"> + <property name="text"> + <string>Reverse shape 2</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="cb_oldsweep"> + <property name="text"> + <string>Old sweep method</string> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>CovererUI</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>CovererUI</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>