Converted Intersector UI, improved handling of failed ext program launches, fixed launch of non-wine programs

Thu, 27 Jun 2013 12:12:53 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Thu, 27 Jun 2013 12:12:53 +0300
changeset 303
8899806d382d
parent 302
08bd2c185b25
child 304
a808f95b6536

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>

mercurial